mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-24 02:29:32 -04:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0266118b7 | ||
|
|
02978dca1e | ||
|
|
a1a119276a | ||
|
|
86a2d1dbc9 |
@@ -16,7 +16,6 @@ use crate::http_server::common_responses::tag_info::TagInfo;
|
||||
use crate::http_server::common_responses::user_details_lite::{UserDefaultAvatarInfo, UserDetailsLight};
|
||||
use crate::http_server::deprecated_endpoints::conversion::enqueue_fbx_to_gltf_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::engine::create_scene_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::image_gen::enqueue_image_generation::EnqueueImageGenRequestError;
|
||||
use crate::http_server::deprecated_endpoints::image_gen::enqueue_image_generation::EnqueueImageGenRequestSuccessResponse;
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_face_fusion_workflow_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_live_portrait_workflow_handler::*;
|
||||
@@ -691,7 +690,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
CreateFeaturedItemError,
|
||||
CreateFeaturedItemRequest,
|
||||
CreateFeaturedItemSuccessResponse,
|
||||
CreateSceneError,
|
||||
CreateSceneSuccessResponse,
|
||||
CreateUserBookmarkError,
|
||||
CreateUserBookmarkRequest,
|
||||
@@ -802,31 +800,23 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
EditUsernameRequest,
|
||||
EditUsernameResponse,
|
||||
EnqueueFaceFusionCropDimensions,
|
||||
EnqueueFaceFusionWorkflowError,
|
||||
EnqueueFaceFusionWorkflowRequest,
|
||||
EnqueueFaceFusionWorkflowSuccessResponse,
|
||||
EnqueueFbxToGltfRequest,
|
||||
EnqueueFbxToGltfRequestError,
|
||||
EnqueueFbxToGltfRequestSuccessResponse,
|
||||
EnqueueGptSovitsModelDownloadError,
|
||||
EnqueueGptSovitsModelDownloadRequest,
|
||||
EnqueueGptSovitsModelDownloadSuccessResponse,
|
||||
EnqueueImageGenRequestError,
|
||||
EnqueueImageGenRequestSuccessResponse,
|
||||
EnqueueLivePortraitCropDimensions,
|
||||
EnqueueLivePortraitWorkflowError,
|
||||
EnqueueLivePortraitWorkflowRequest,
|
||||
EnqueueLivePortraitWorkflowSuccessResponse,
|
||||
EnqueueStudioGen2Error,
|
||||
EnqueueStudioGen2Request,
|
||||
EnqueueStudioGen2Response,
|
||||
EnqueueTTSRequest,
|
||||
EnqueueTTSRequestError,
|
||||
EnqueueTTSRequestSuccessResponse,
|
||||
EnqueueVideoStyleTransferError,
|
||||
EnqueueVideoStyleTransferRequest,
|
||||
EnqueueVideoStyleTransferSuccessResponse,
|
||||
EnqueueVoiceConversionInferenceError,
|
||||
EnqueueVoiceConversionInferenceRequest,
|
||||
EnqueueVoiceConversionInferenceSuccessResponse,
|
||||
FeaturedMediaFile,
|
||||
@@ -993,7 +983,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
ProfileError,
|
||||
PromptInfo,
|
||||
RatingRow,
|
||||
RedeemBetaKeyError,
|
||||
RedeemBetaKeyRequest,
|
||||
RedeemBetaKeySuccessResponse,
|
||||
RemoveImageBackgroundRequest,
|
||||
|
||||
+20
@@ -68,6 +68,9 @@ pub enum CommonWebError {
|
||||
/// 403 Forbidden — content was rejected, with a user-facing message.
|
||||
ContentPolicyRejectedWithMessage(String),
|
||||
|
||||
/// 429 Too Many Requests - user is sending too many requests and is being rate limited.
|
||||
TooManyRequests,
|
||||
|
||||
/// Uncaught errors are always 500 Internal Server Error.
|
||||
/// The user will never see the error cause or message, but our
|
||||
/// middleware will handle alerting, logging, etc.
|
||||
@@ -174,6 +177,7 @@ impl Display for CommonWebError {
|
||||
Self::Forbidden => write!(f, "Forbidden"),
|
||||
Self::ContentPolicyRejected => write!(f, "Content policy rejected"),
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => write!(f, "Content policy rejected: {}", msg),
|
||||
Self::TooManyRequests => write!(f, "Too many requests"),
|
||||
Self::UncaughtServerError(err) => write!(f, "Server error: {}", err),
|
||||
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
|
||||
write!(f, "Server error: {}: {}", internal_message, error)
|
||||
@@ -198,6 +202,7 @@ impl std::fmt::Debug for CommonWebError {
|
||||
Self::Forbidden => write!(f, "Forbidden"),
|
||||
Self::ContentPolicyRejected => write!(f, "ContentPolicyRejected"),
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => write!(f, "ContentPolicyRejectedWithMessage({:?})", msg),
|
||||
Self::TooManyRequests => write!(f, "TooManyRequests"),
|
||||
Self::UncaughtServerError(err) => write!(f, "UncaughtServerError({:?})", err),
|
||||
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
|
||||
write!(f, "UncaughtServerErrorWithInternalMessage({:?}, {:?})", internal_message, error)
|
||||
@@ -229,6 +234,7 @@ impl ResponseError for CommonWebError {
|
||||
Self::Forbidden => StatusCode::FORBIDDEN,
|
||||
Self::ContentPolicyRejected => StatusCode::FORBIDDEN,
|
||||
Self::ContentPolicyRejectedWithMessage(_) => StatusCode::FORBIDDEN,
|
||||
Self::TooManyRequests => StatusCode::TOO_MANY_REQUESTS,
|
||||
Self::UncaughtServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::UncaughtServerErrorWithInternalMessage { .. } => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
@@ -479,6 +485,20 @@ mod tests {
|
||||
assert_eq!(error.status_code(), StatusCode::PAYMENT_REQUIRED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_requests_returns_429() {
|
||||
let error = CommonWebError::TooManyRequests;
|
||||
assert_eq!(error.status_code(), StatusCode::TOO_MANY_REQUESTS);
|
||||
assert!(!error.is_server_error());
|
||||
assert!(error.cause().is_none());
|
||||
|
||||
let response = error.error_response();
|
||||
let bytes = response.into_body().try_into_bytes().unwrap();
|
||||
let body = String::from_utf8(bytes.to_vec()).unwrap();
|
||||
assert!(body.contains("\"error_code\":429"));
|
||||
assert!(body.contains("\"error_code_str\":\"Too Many Requests\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uncaught_io_error_returns_500_and_hides_cause() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk exploded");
|
||||
|
||||
+17
-48
@@ -20,40 +20,9 @@ use mysql_queries::queries::media_files::create::specialized_insert::insert_medi
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::deprecated_endpoints::engine::drain_multipart_request::drain_multipart_request;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub enum CreateSceneError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
MustBeLoggedIn,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for CreateSceneError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
CreateSceneError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
CreateSceneError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
CreateSceneError::MustBeLoggedIn => StatusCode::UNAUTHORIZED,
|
||||
CreateSceneError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CreateSceneError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CreateSceneError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct CreateSceneSuccessResponse {
|
||||
pub success: bool,
|
||||
@@ -68,22 +37,22 @@ pub struct CreateSceneSuccessResponse {
|
||||
path = "/v1/engine/create_scene",
|
||||
responses(
|
||||
(status = 200, description = "Found", body = CreateSceneSuccessResponse),
|
||||
(status = 404, description = "Not found", body = CreateSceneError),
|
||||
(status = 500, description = "Server error", body = CreateSceneError),
|
||||
(status = 404, description = "Not found", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError),
|
||||
),
|
||||
)]
|
||||
pub async fn create_scene_handler(
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
mut multipart_payload: Multipart,
|
||||
) -> Result<HttpResponse, CreateSceneError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("MySql pool error: {:?}", err);
|
||||
CreateSceneError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== READ SESSION ==================== //
|
||||
@@ -94,7 +63,7 @@ pub async fn create_scene_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Session checker error: {:?}", e);
|
||||
CreateSceneError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -105,7 +74,7 @@ pub async fn create_scene_handler(
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.is_banned {
|
||||
return Err(CreateSceneError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +86,7 @@ pub async fn create_scene_handler(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(CreateSceneError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== READ MULTIPART REQUEST ==================== //
|
||||
@@ -126,16 +95,16 @@ pub async fn create_scene_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
// TODO: Error handling could be nicer.
|
||||
CreateSceneError::BadInput("bad request".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("bad request".to_string())
|
||||
})?;
|
||||
|
||||
let uuid_idempotency_token = upload_media_request.uuid_idempotency_token
|
||||
.ok_or(CreateSceneError::BadInput("no uuid".to_string()))?;
|
||||
.ok_or(CommonWebError::BadInputWithSimpleMessage("no uuid".to_string()))?;
|
||||
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&uuid_idempotency_token) {
|
||||
return Err(CreateSceneError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
// TODO(bt, 2024-02-22): This should be a transaction.
|
||||
@@ -143,7 +112,7 @@ pub async fn create_scene_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
CreateSceneError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== USER DATA ==================== //
|
||||
@@ -164,7 +133,7 @@ pub async fn create_scene_handler(
|
||||
.unwrap_or("text/plain");
|
||||
|
||||
let bytes = match upload_media_request.file_bytes {
|
||||
None => return Err(CreateSceneError::BadInput("missing file contents".to_string())),
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("missing file contents".to_string())),
|
||||
Some(bytes) => bytes,
|
||||
};
|
||||
|
||||
@@ -173,7 +142,7 @@ pub async fn create_scene_handler(
|
||||
let hash = sha256_hash_bytes(&bytes)
|
||||
.map_err(|io_error| {
|
||||
error!("Problem hashing bytes: {:?}", io_error);
|
||||
CreateSceneError::ServerError
|
||||
CommonWebError::from_anyhow_error(io_error)
|
||||
})?;
|
||||
|
||||
const PREFIX : Option<&str> = Some("scene_");
|
||||
@@ -190,7 +159,7 @@ pub async fn create_scene_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Upload media bytes to bucket error: {:?}", e);
|
||||
CreateSceneError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
// TODO(bt, 2024-02-22): This should be a transaction.
|
||||
@@ -222,7 +191,7 @@ pub async fn create_scene_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("New file creation DB error: {:?}", err);
|
||||
CreateSceneError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
info!("new media file id: {} token: {:?}", record_id, &token);
|
||||
@@ -233,7 +202,7 @@ pub async fn create_scene_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| CreateSceneError::ServerError)?;
|
||||
.map_err(CommonWebError::from_error)?;
|
||||
|
||||
return Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+23
-59
@@ -37,6 +37,7 @@ use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
@@ -138,43 +139,6 @@ pub struct EnqueueImageGenRequestSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueImageGenRequestError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueImageGenRequestError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueImageGenRequestError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueImageGenRequestError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueImageGenRequestError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueImageGenRequestError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueImageGenRequestError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueImageGenRequestError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueImageGenRequestError::ServerError => "server error".to_string(),
|
||||
EnqueueImageGenRequestError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EnqueueImageGenRequestError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for enqueuing a TTS request
|
||||
// Reference enqueue_infer_tts_handler.rs for checks: rate limiting / user sessions
|
||||
// insert generic inference job.rs
|
||||
@@ -188,10 +152,10 @@ impl Display for EnqueueImageGenRequestError {
|
||||
description = "Enqueue TTS generically",
|
||||
body = EnqueueImageGenRequestSuccessResponse,
|
||||
),
|
||||
(status = 400, description = "Bad input", body = EnqueueImageGenRequestError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueImageGenRequestError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueImageGenRequestError),
|
||||
(status = 500, description = "Server error", body = EnqueueImageGenRequestError)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(("request" = EnqueueImageGenRequest, description = "Payload for TTS Request"))
|
||||
)]
|
||||
@@ -199,10 +163,10 @@ pub async fn enqueue_image_generation_request(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueImageGenRequest>,
|
||||
server_state: Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, EnqueueImageGenRequestError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let inference_mode = inference_mode_from_http_request(&http_request)
|
||||
.ok_or(EnqueueImageGenRequestError::BadInput("Invalid request".to_string()))?;
|
||||
.ok_or(CommonWebError::BadInputWithSimpleMessage("Invalid request".to_string()))?;
|
||||
|
||||
validate_request(&request, inference_mode)?;
|
||||
|
||||
@@ -213,7 +177,7 @@ pub async fn enqueue_image_generation_request(
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueImageGenRequestError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -222,7 +186,7 @@ pub async fn enqueue_image_generation_request(
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, &mut mysql_connection).await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueImageGenRequestError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
|
||||
@@ -254,7 +218,7 @@ pub async fn enqueue_image_generation_request(
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueImageGenRequestError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +232,7 @@ pub async fn enqueue_image_generation_request(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueImageGenRequestError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// Get up IP address
|
||||
@@ -279,14 +243,14 @@ pub async fn enqueue_image_generation_request(
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueImageGenRequestError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueImageGenRequestError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== INFERENCE ARGS ==================== //
|
||||
@@ -349,7 +313,7 @@ pub async fn enqueue_image_generation_request(
|
||||
|
||||
match both_fields {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Can't upload both lora and model".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Can't upload both lora and model".to_string()));
|
||||
}
|
||||
(Some(sd_url), None) => {
|
||||
maybe_sd_upload_url = Some(sd_url.to_string());
|
||||
@@ -438,9 +402,9 @@ pub async fn enqueue_image_generation_request(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueImageGenRequestError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -451,7 +415,7 @@ pub async fn enqueue_image_generation_request(
|
||||
|
||||
let body = serde_json
|
||||
::to_string(&response)
|
||||
.map_err(|_e| EnqueueImageGenRequestError::ServerError)?;
|
||||
?;
|
||||
|
||||
// Error handling 101 rust result type returned like so.
|
||||
Ok(HttpResponse::Ok().content_type("application/json").body(body))
|
||||
@@ -460,7 +424,7 @@ pub async fn enqueue_image_generation_request(
|
||||
fn validate_request(
|
||||
request: &Json<EnqueueImageGenRequest>,
|
||||
inference_mode: TypeOfInference
|
||||
) -> Result<(), EnqueueImageGenRequestError> {
|
||||
) -> Result<(), CommonWebError> {
|
||||
|
||||
let mut requires_name = false;
|
||||
let mut requires_description = false;
|
||||
@@ -485,19 +449,19 @@ fn validate_request(
|
||||
}
|
||||
|
||||
if requires_name && request.maybe_name.is_none() {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Missing Model / Lora Name".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Model / Lora Name".to_string()));
|
||||
}
|
||||
if requires_description && request.maybe_description.is_none() {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Missing Model / Lora Description".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Model / Lora Description".to_string()));
|
||||
}
|
||||
if requires_sd_model_token && request.maybe_sd_model_token.is_none() {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Missing Model Token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Model Token".to_string()));
|
||||
}
|
||||
if requires_lora_upload_path && request.maybe_lora_upload_path.is_none() {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Missing Lora Upload Path".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Lora Upload Path".to_string()));
|
||||
}
|
||||
if requires_upload_path && request.maybe_upload_path.is_none() {
|
||||
return Err(EnqueueImageGenRequestError::BadInput("Missing Model Upload Path".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Model Upload Path".to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+14
-50
@@ -21,6 +21,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::configs::plans::plan_category::PlanCategory;
|
||||
use crate::http_server::requests::get_request_domain_branding::{get_request_domain_branding, DomainBranding};
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
@@ -75,44 +76,7 @@ pub struct EnqueueFaceFusionWorkflowSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueFaceFusionWorkflowError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueFaceFusionWorkflowError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueFaceFusionWorkflowError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueFaceFusionWorkflowError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueFaceFusionWorkflowError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueFaceFusionWorkflowError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueFaceFusionWorkflowError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueFaceFusionWorkflowError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueFaceFusionWorkflowError::ServerError => "server error".to_string(),
|
||||
EnqueueFaceFusionWorkflowError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl std::fmt::Display for EnqueueFaceFusionWorkflowError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enqueue "lipsync" (Face Fusion) video workflows.
|
||||
///
|
||||
/// We've renamed this as to not give away what we're doing to users.
|
||||
@@ -122,10 +86,10 @@ impl std::fmt::Display for EnqueueFaceFusionWorkflowError {
|
||||
path = "/v1/workflows/enqueue_lipsync",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = EnqueueFaceFusionWorkflowSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = EnqueueFaceFusionWorkflowError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueFaceFusionWorkflowError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueFaceFusionWorkflowError),
|
||||
(status = 500, description = "Server error", body = EnqueueFaceFusionWorkflowError)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(("request" = EnqueueFaceFusionWorkflowRequest, description = "Payload for request"))
|
||||
)]
|
||||
@@ -133,7 +97,7 @@ pub async fn enqueue_face_fusion_workflow_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueFaceFusionWorkflowRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<EnqueueFaceFusionWorkflowSuccessResponse>, EnqueueFaceFusionWorkflowError>
|
||||
) -> Result<Json<EnqueueFaceFusionWorkflowSuccessResponse>, CommonWebError>
|
||||
{
|
||||
// ==================== DB ==================== //
|
||||
|
||||
@@ -142,7 +106,7 @@ pub async fn enqueue_face_fusion_workflow_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueFaceFusionWorkflowError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -156,8 +120,8 @@ pub async fn enqueue_face_fusion_workflow_handler(
|
||||
&mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => EnqueueFaceFusionWorkflowError::ServerError,
|
||||
RequireUserSessionError::NotAuthorized => EnqueueFaceFusionWorkflowError::NotAuthorized,
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
@@ -179,20 +143,20 @@ pub async fn enqueue_face_fusion_workflow_handler(
|
||||
// ==================== RATE LIMIT ==================== //
|
||||
|
||||
if let Err(_err) = server_state.redis_rate_limiters.logged_in.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueFaceFusionWorkflowError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueFaceFusionWorkflowError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueFaceFusionWorkflowError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== HANDLE REQUEST ==================== //
|
||||
@@ -275,9 +239,9 @@ pub async fn enqueue_face_fusion_workflow_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueFaceFusionWorkflowError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueFaceFusionWorkflowError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+14
-50
@@ -22,6 +22,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::configs::plans::plan_category::PlanCategory;
|
||||
use crate::http_server::requests::get_request_domain_branding::{get_request_domain_branding, DomainBranding};
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
@@ -84,44 +85,7 @@ pub struct EnqueueLivePortraitWorkflowSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueLivePortraitWorkflowError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueLivePortraitWorkflowError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueLivePortraitWorkflowError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueLivePortraitWorkflowError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueLivePortraitWorkflowError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueLivePortraitWorkflowError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueLivePortraitWorkflowError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueLivePortraitWorkflowError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueLivePortraitWorkflowError::ServerError => "server error".to_string(),
|
||||
EnqueueLivePortraitWorkflowError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl std::fmt::Display for EnqueueLivePortraitWorkflowError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enqueue "face mirror" (Live Portrait) video workflows.
|
||||
///
|
||||
/// We've renamed this as to not give away what we're doing to users.
|
||||
@@ -131,17 +95,17 @@ impl std::fmt::Display for EnqueueLivePortraitWorkflowError {
|
||||
path = "/v1/workflows/enqueue_face_mirror",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = EnqueueLivePortraitWorkflowSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = EnqueueLivePortraitWorkflowError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueLivePortraitWorkflowError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueLivePortraitWorkflowError),
|
||||
(status = 500, description = "Server error", body = EnqueueLivePortraitWorkflowError)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(("request" = EnqueueLivePortraitWorkflowRequest, description = "Payload for request"))
|
||||
)]
|
||||
pub async fn enqueue_live_portrait_workflow_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueLivePortraitWorkflowRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<Json<EnqueueLivePortraitWorkflowSuccessResponse>, EnqueueLivePortraitWorkflowError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<Json<EnqueueLivePortraitWorkflowSuccessResponse>, CommonWebError>
|
||||
{
|
||||
// ==================== DB ==================== //
|
||||
|
||||
@@ -150,7 +114,7 @@ pub async fn enqueue_live_portrait_workflow_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueLivePortraitWorkflowError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -164,8 +128,8 @@ pub async fn enqueue_live_portrait_workflow_handler(
|
||||
&mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => EnqueueLivePortraitWorkflowError::ServerError,
|
||||
RequireUserSessionError::NotAuthorized => EnqueueLivePortraitWorkflowError::NotAuthorized,
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
@@ -187,20 +151,20 @@ pub async fn enqueue_live_portrait_workflow_handler(
|
||||
// ==================== RATE LIMIT ==================== //
|
||||
|
||||
if let Err(_err) = server_state.redis_rate_limiters.logged_in.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueLivePortraitWorkflowError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueLivePortraitWorkflowError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueLivePortraitWorkflowError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== HANDLE REQUEST ==================== //
|
||||
@@ -293,9 +257,9 @@ pub async fn enqueue_live_portrait_workflow_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueLivePortraitWorkflowError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueLivePortraitWorkflowError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+12
-51
@@ -6,8 +6,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::{error, info, warn};
|
||||
|
||||
@@ -30,12 +28,12 @@ use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::configs::plans::plan_category::PlanCategory;
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::session::lookup::user_session_extended::UserSessionExtended;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::allowed_video_style_transfer_access::allowed_video_style_transfer_access;
|
||||
|
||||
@@ -68,48 +66,11 @@ pub struct EnqueueComfySuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EnqueueComfyError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueComfyError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueComfyError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueComfyError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueComfyError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueComfyError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueComfyError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueComfyError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueComfyError::ServerError => "server error".to_string(),
|
||||
EnqueueComfyError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl std::fmt::Display for EnqueueComfyError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn enqueue_comfy_ui_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<EnqueueComfyRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, EnqueueComfyError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let mut maybe_user_token : Option<UserToken> = None;
|
||||
|
||||
@@ -118,7 +79,7 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueComfyError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
@@ -132,7 +93,7 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueComfyError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
if let Some(user_session) = maybe_user_session.as_ref() {
|
||||
@@ -143,7 +104,7 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
|
||||
if !allowed_video_style_transfer_access(maybe_user_session.as_ref(), &server_state.flags) {
|
||||
warn!("Video style transfer access is not permitted for user");
|
||||
return Err(EnqueueComfyError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
@@ -168,27 +129,27 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
None => &server_state.redis_rate_limiters.logged_out,
|
||||
Some(ref user) => {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueComfyError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
&server_state.redis_rate_limiters.logged_in
|
||||
},
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueComfyError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueComfyError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueComfyError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== LOOK UP MODEL INFO ==================== //
|
||||
@@ -327,9 +288,9 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueComfyError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueComfyError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -339,7 +300,7 @@ pub async fn enqueue_comfy_ui_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| EnqueueComfyError::ServerError)?;
|
||||
?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+18
-57
@@ -5,8 +5,6 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::{error, info, warn};
|
||||
use utoipa::ToSchema;
|
||||
@@ -30,13 +28,13 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::configs::plans::plan_category::PlanCategory;
|
||||
use crate::http_server::deprecated_endpoints::workflows::coordinate_workflow_args::{coordinate_workflow_args, CoordinatedWorkflowArgs};
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::session::lookup::user_session_extended::UserSessionExtended;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::allowed_video_style_transfer_access::allowed_video_style_transfer_access;
|
||||
use crate::util::cleaners::empty_media_file_token_to_null::empty_media_file_token_to_null;
|
||||
@@ -166,44 +164,7 @@ pub struct EnqueueVideoStyleTransferSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueVideoStyleTransferError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueVideoStyleTransferError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueVideoStyleTransferError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueVideoStyleTransferError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueVideoStyleTransferError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueVideoStyleTransferError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueVideoStyleTransferError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueVideoStyleTransferError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueVideoStyleTransferError::ServerError => "server error".to_string(),
|
||||
EnqueueVideoStyleTransferError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl std::fmt::Display for EnqueueVideoStyleTransferError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// DEPRECATED. Use /v1/workflows/enqueue_studio or /v1/workflows/enqueue_vst
|
||||
#[deprecated(note = "Use /v1/workflows/enqueue_studio")]
|
||||
#[utoipa::path(
|
||||
@@ -212,24 +173,24 @@ impl std::fmt::Display for EnqueueVideoStyleTransferError {
|
||||
path = "/v1/video/enqueue_vst",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = EnqueueVideoStyleTransferSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = EnqueueVideoStyleTransferError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueVideoStyleTransferError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueVideoStyleTransferError),
|
||||
(status = 500, description = "Server error", body = EnqueueVideoStyleTransferError)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(("request" = EnqueueVideoStyleTransferRequest, description = "Payload for request"))
|
||||
)]
|
||||
pub async fn enqueue_video_style_transfer_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<EnqueueVideoStyleTransferRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, EnqueueVideoStyleTransferError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
// ==================== VALIDATION ==================== //
|
||||
|
||||
match request.frame_skip {
|
||||
None | Some(1) | Some(2) => {} // Allowed
|
||||
_ => {
|
||||
return Err(EnqueueVideoStyleTransferError::BadInput("Invalid frame skip value".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Invalid frame skip value".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +201,7 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueVideoStyleTransferError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
@@ -254,7 +215,7 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueVideoStyleTransferError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
@@ -265,7 +226,7 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
|
||||
if !allowed_video_style_transfer_access(maybe_user_session.as_ref(), &server_state.flags) {
|
||||
warn!("Video style transfer access is not permitted for user");
|
||||
return Err(EnqueueVideoStyleTransferError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
@@ -295,27 +256,27 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
None => &server_state.redis_rate_limiters.logged_out,
|
||||
Some(ref user) => {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueVideoStyleTransferError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
&server_state.redis_rate_limiters.logged_in
|
||||
},
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueVideoStyleTransferError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueVideoStyleTransferError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueVideoStyleTransferError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== LOOK UP MODEL INFO ==================== //
|
||||
@@ -353,7 +314,7 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
let maybe_strength = request.use_strength
|
||||
.map(|strength| {
|
||||
if strength < MINIMUM_STRENGTH || strength > MAXIMUM_STRENGTH {
|
||||
Err(EnqueueVideoStyleTransferError::BadInput("Strength must be between 0.0 and 1.0".to_string()))
|
||||
Err(CommonWebError::BadInputWithSimpleMessage("Strength must be between 0.0 and 1.0".to_string()))
|
||||
} else {
|
||||
Ok(strength)
|
||||
}
|
||||
@@ -481,9 +442,9 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueVideoStyleTransferError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueVideoStyleTransferError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -493,7 +454,7 @@ pub async fn enqueue_video_style_transfer_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| EnqueueVideoStyleTransferError::ServerError)?;
|
||||
?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+15
-51
@@ -39,6 +39,7 @@ use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
@@ -62,43 +63,6 @@ pub struct EnqueueWorkFlowRequestSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueWorkFlowRequestError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueWorkFlowRequestError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueWorkFlowRequestError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueWorkFlowRequestError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueWorkFlowRequestError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueWorkFlowRequestError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueWorkFlowRequestError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueWorkFlowRequestError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueWorkFlowRequestError::ServerError => "server error".to_string(),
|
||||
EnqueueWorkFlowRequestError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EnqueueWorkFlowRequestError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for enqueuing a TTS request
|
||||
// Reference enqueue_infer_tts_handler.rs for checks: rate limiting / user sessions
|
||||
// insert generic inference job.rs
|
||||
@@ -108,23 +72,23 @@ pub async fn enqueue_workflow_upload_request(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueWorkFlowRequest>,
|
||||
server_state: Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, EnqueueWorkFlowRequestError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
|
||||
if request.google_drive_link.is_empty() {
|
||||
return Err(EnqueueWorkFlowRequestError::BadInput("Missing Google Drive Link".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Google Drive Link".to_string()));
|
||||
}
|
||||
|
||||
if request.title.is_empty() {
|
||||
return Err(EnqueueWorkFlowRequestError::BadInput("Missing Title".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Title".to_string()));
|
||||
}
|
||||
|
||||
if request.description.is_empty() {
|
||||
return Err(EnqueueWorkFlowRequestError::BadInput("Missing Description".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing Description".to_string()));
|
||||
}
|
||||
|
||||
if request.commit_hash.is_empty() {
|
||||
return Err(EnqueueWorkFlowRequestError::BadInput("Missing commit hash".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing commit hash".to_string()));
|
||||
}
|
||||
|
||||
let mut maybe_user_token: Option<UserToken> = None;
|
||||
@@ -134,7 +98,7 @@ pub async fn enqueue_workflow_upload_request(
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueWorkFlowRequestError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -143,7 +107,7 @@ pub async fn enqueue_workflow_upload_request(
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, &mut mysql_connection).await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueWorkFlowRequestError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
|
||||
@@ -172,7 +136,7 @@ pub async fn enqueue_workflow_upload_request(
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueWorkFlowRequestError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +150,7 @@ pub async fn enqueue_workflow_upload_request(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueWorkFlowRequestError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// Get up IP address
|
||||
@@ -197,14 +161,14 @@ pub async fn enqueue_workflow_upload_request(
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueWorkFlowRequestError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueWorkFlowRequestError::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== INFERENCE ARGS ==================== //
|
||||
@@ -300,9 +264,9 @@ pub async fn enqueue_workflow_upload_request(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueWorkFlowRequestError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueWorkFlowRequestError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -313,7 +277,7 @@ pub async fn enqueue_workflow_upload_request(
|
||||
|
||||
let body = serde_json
|
||||
::to_string(&response)
|
||||
.map_err(|_e| EnqueueWorkFlowRequestError::ServerError)?;
|
||||
?;
|
||||
|
||||
// Error handling 101 rust result type returned like so.
|
||||
Ok(HttpResponse::Ok().content_type("application/json").body(body))
|
||||
|
||||
+23
-76
@@ -1,12 +1,10 @@
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::warn;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags;
|
||||
use enums::by_table::beta_keys::beta_key_product::BetaKeyProduct;
|
||||
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
|
||||
@@ -17,9 +15,8 @@ use mysql_queries::queries::users::user::update::set_can_access_studio_transacti
|
||||
use mysql_queries::queries::users::user::update::set_user_feature_flags_transactional::{set_user_feature_flags_transactional, SetUserFeatureFlagTransactionalArgs};
|
||||
use mysql_queries::queries::users::user_sessions::get_user_session_by_token::SessionUserRecord;
|
||||
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::try_delete_session_cache::try_delete_session_cache;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::{require_user_session, RequireUserSessionError};
|
||||
use crate::http_server::web_utils::user_session::require_user_session::require_user_session;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
@@ -32,46 +29,6 @@ pub struct RedeemBetaKeySuccessResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum RedeemBetaKeyError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
RateLimited,
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for RedeemBetaKeyError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
RedeemBetaKeyError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
RedeemBetaKeyError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
RedeemBetaKeyError::NotFound => StatusCode::NOT_FOUND,
|
||||
RedeemBetaKeyError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
RedeemBetaKeyError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
RedeemBetaKeyError::BadInput(reason) => reason.to_string(),
|
||||
RedeemBetaKeyError::NotAuthorized => "unauthorized".to_string(),
|
||||
RedeemBetaKeyError::NotFound => "not found".to_string(),
|
||||
RedeemBetaKeyError::RateLimited => "rate limited".to_string(),
|
||||
RedeemBetaKeyError::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for RedeemBetaKeyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Redeem a beta key to gain access to a feature
|
||||
#[utoipa::path(
|
||||
post,
|
||||
@@ -79,11 +36,11 @@ impl fmt::Display for RedeemBetaKeyError {
|
||||
path = "/v1/beta_keys/redeem",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = RedeemBetaKeySuccessResponse),
|
||||
(status = 400, description = "Bad input", body = RedeemBetaKeyError),
|
||||
(status = 401, description = "Not authorized", body = RedeemBetaKeyError),
|
||||
(status = 404, description = "Not found", body = RedeemBetaKeyError),
|
||||
(status = 429, description = "Rate limited", body = RedeemBetaKeyError),
|
||||
(status = 500, description = "Server error", body = RedeemBetaKeyError),
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 404, description = "Not found", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError),
|
||||
),
|
||||
params(
|
||||
("request" = RedeemBetaKeyRequest, description = "Payload for Request"),
|
||||
@@ -93,47 +50,37 @@ pub async fn redeem_beta_key_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<RedeemBetaKeyRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, RedeemBetaKeyError>
|
||||
) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let user_session = require_user_session(&http_request, &server_state)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => RedeemBetaKeyError::ServerError,
|
||||
RequireUserSessionError::NotAuthorized => RedeemBetaKeyError::NotAuthorized,
|
||||
})?;
|
||||
let user_session = require_user_session(&http_request, &server_state).await?;
|
||||
|
||||
let rate_limiter = &server_state.redis_rate_limiters.logged_out;
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(RedeemBetaKeyError::RateLimited);
|
||||
if rate_limiter.rate_limit_request(&http_request).await.is_err() {
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
let maybe_beta_key = get_beta_key_by_value(&request.beta_key, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error getting beta key by value: {:?}", &err);
|
||||
RedeemBetaKeyError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let beta_key = match maybe_beta_key {
|
||||
Some(beta_key) => beta_key,
|
||||
None => return Err(RedeemBetaKeyError::NotFound),
|
||||
None => return Err(CommonWebError::NotFound),
|
||||
};
|
||||
|
||||
if beta_key.maybe_redeemed_at.is_some() || beta_key.maybe_redeemer_user_token.is_some() {
|
||||
return Err(RedeemBetaKeyError::BadInput("beta key already redeemed".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("beta key already redeemed".to_string()));
|
||||
}
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
match beta_key.product {
|
||||
BetaKeyProduct::Studio => {
|
||||
enroll_in_studio(&request, &server_state, &user_session, &ip_address)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error enrolling in studio: {:?}", &err);
|
||||
RedeemBetaKeyError::ServerError
|
||||
})?;
|
||||
enroll_in_studio(&request, &server_state, &user_session, &ip_address).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +90,8 @@ pub async fn redeem_beta_key_handler(
|
||||
success: true,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| RedeemBetaKeyError::ServerError)?;
|
||||
// `?` via the `From<serde_json::Error>` impl on CommonWebError.
|
||||
let body = serde_json::to_string(&response)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
@@ -156,7 +103,7 @@ async fn enroll_in_studio(
|
||||
server_state: &ServerState,
|
||||
user_session: &SessionUserRecord,
|
||||
ip_address: &str,
|
||||
) -> Result<(), RedeemBetaKeyError> {
|
||||
) -> Result<(), CommonWebError> {
|
||||
let mut user_feature_flags =
|
||||
UserSessionFeatureFlags::new(user_session.maybe_feature_flags.as_deref());
|
||||
|
||||
@@ -169,7 +116,7 @@ async fn enroll_in_studio(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Could not open transaction: {:?}", e);
|
||||
RedeemBetaKeyError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
set_user_feature_flags_transactional(SetUserFeatureFlagTransactionalArgs {
|
||||
@@ -181,7 +128,7 @@ async fn enroll_in_studio(
|
||||
}).await
|
||||
.map_err(|e| {
|
||||
warn!("Could not set flags: {:?}", e);
|
||||
RedeemBetaKeyError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
// NB: This isn't a necessary field, but can be useful for analytics.
|
||||
@@ -192,21 +139,21 @@ async fn enroll_in_studio(
|
||||
}).await
|
||||
.map_err(|e| {
|
||||
warn!("Could not set can_access_studio: {:?}", e);
|
||||
RedeemBetaKeyError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
redeem_beta_key(&request.beta_key, &user_session.user_token, &mut transaction)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Could not redeem beta key: {:?}", e);
|
||||
RedeemBetaKeyError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
transaction.commit()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Could not commit transaction: {:?}", e);
|
||||
RedeemBetaKeyError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+13
-42
@@ -23,6 +23,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
@@ -50,36 +51,6 @@ pub struct EnqueueGptSovitsModelDownloadSuccessResponse {
|
||||
/// This is how frontend clients can request the job execution status.
|
||||
pub job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub enum EnqueueGptSovitsModelDownloadError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueGptSovitsModelDownloadError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueGptSovitsModelDownloadError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueGptSovitsModelDownloadError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueGptSovitsModelDownloadError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueGptSovitsModelDownloadError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnqueueGptSovitsModelDownloadError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enqueue a GptSoVits model for download (eg. from Google Drive)
|
||||
#[utoipa::path(
|
||||
post,
|
||||
@@ -88,13 +59,13 @@ impl fmt::Display for EnqueueGptSovitsModelDownloadError {
|
||||
request_body = EnqueueGptSovitsModelDownloadRequest,
|
||||
responses(
|
||||
(status = 200, body = EnqueueGptSovitsModelDownloadSuccessResponse),
|
||||
(status = 400, body = EnqueueGptSovitsModelDownloadError),
|
||||
(status = 400, body = CommonWebError),
|
||||
)
|
||||
)]
|
||||
pub async fn enqueue_gptsovits_model_download_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueGptSovitsModelDownloadRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<Json<EnqueueGptSovitsModelDownloadSuccessResponse>, EnqueueGptSovitsModelDownloadError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<Json<EnqueueGptSovitsModelDownloadSuccessResponse>, CommonWebError>
|
||||
{
|
||||
// ==================== DB ==================== //
|
||||
|
||||
@@ -103,7 +74,7 @@ pub async fn enqueue_gptsovits_model_download_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueGptSovitsModelDownloadError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -117,8 +88,8 @@ pub async fn enqueue_gptsovits_model_download_handler(
|
||||
&mut mysql_connection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireUserSessionError::ServerError => EnqueueGptSovitsModelDownloadError::ServerError,
|
||||
RequireUserSessionError::NotAuthorized => EnqueueGptSovitsModelDownloadError::NotAuthorized,
|
||||
RequireUserSessionError::ServerError => CommonWebError::from_error(err),
|
||||
RequireUserSessionError::NotAuthorized => CommonWebError::NotAuthorized,
|
||||
})?;
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
@@ -133,7 +104,7 @@ pub async fn enqueue_gptsovits_model_download_handler(
|
||||
let maybe_routing_tag= get_routing_tag_header(&http_request);
|
||||
|
||||
if let Err(_err) = server_state.redis_rate_limiters.model_upload.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueGptSovitsModelDownloadError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
@@ -145,23 +116,23 @@ pub async fn enqueue_gptsovits_model_download_handler(
|
||||
let creator_set_visibility = request.creator_set_visibility.unwrap_or(Visibility::Public);
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&uuid) {
|
||||
return Err(EnqueueGptSovitsModelDownloadError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
if let Some(title) = title {
|
||||
if let Err(reason) = validate_model_title(title) {
|
||||
return Err(EnqueueGptSovitsModelDownloadError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
}
|
||||
|
||||
match is_bad_tts_model_download_url(&download_url) {
|
||||
Ok(false) => {} // Ok case
|
||||
Ok(true) => {
|
||||
return Err(EnqueueGptSovitsModelDownloadError::BadInput("Bad model download URL".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Bad model download URL".to_string()));
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Error parsing url: {:?}", err);
|
||||
return Err(EnqueueGptSovitsModelDownloadError::BadInput("Bad model download URL".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Bad model download URL".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,9 +177,9 @@ pub async fn enqueue_gptsovits_model_download_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueGptSovitsModelDownloadError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueGptSovitsModelDownloadError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+15
-51
@@ -26,6 +26,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::configs::plans::plan_category::PlanCategory;
|
||||
use crate::http_server::requests::request_headers::get_routing_tag_header::get_routing_tag_header;
|
||||
use crate::http_server::requests::request_headers::has_debug_header::has_debug_header;
|
||||
@@ -84,44 +85,7 @@ pub struct EnqueueStudioGen2Response {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueStudioGen2Error {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueStudioGen2Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueStudioGen2Error::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueStudioGen2Error::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueStudioGen2Error::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueStudioGen2Error::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueStudioGen2Error::BadInput(reason) => reason.to_string(),
|
||||
EnqueueStudioGen2Error::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueStudioGen2Error::ServerError => "server error".to_string(),
|
||||
EnqueueStudioGen2Error::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl std::fmt::Display for EnqueueStudioGen2Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enqueue Studio Gen2 jobs.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
@@ -129,10 +93,10 @@ impl std::fmt::Display for EnqueueStudioGen2Error {
|
||||
path = "/v1/studio_gen2/enqueue",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = EnqueueStudioGen2Response),
|
||||
(status = 400, description = "Bad input", body = EnqueueStudioGen2Error),
|
||||
(status = 401, description = "Not authorized", body = EnqueueStudioGen2Error),
|
||||
(status = 429, description = "Rate limited", body = EnqueueStudioGen2Error),
|
||||
(status = 500, description = "Server error", body = EnqueueStudioGen2Error)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(("request" = EnqueueStudioGen2Request, description = "Payload for request"))
|
||||
)]
|
||||
@@ -140,7 +104,7 @@ pub async fn enqueue_studio_gen2_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueStudioGen2Request>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<EnqueueStudioGen2Response>, EnqueueStudioGen2Error> {
|
||||
) -> Result<Json<EnqueueStudioGen2Response>, CommonWebError> {
|
||||
|
||||
let a = 1;
|
||||
// ==================== DB ==================== //
|
||||
@@ -150,7 +114,7 @@ pub async fn enqueue_studio_gen2_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueStudioGen2Error::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state.avt_cookie_manager
|
||||
@@ -164,7 +128,7 @@ pub async fn enqueue_studio_gen2_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueStudioGen2Error::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_user_token = maybe_user_session
|
||||
@@ -175,7 +139,7 @@ pub async fn enqueue_studio_gen2_handler(
|
||||
|
||||
//if !allowed_video_style_transfer_access(maybe_user_session.as_ref(), &server_state.flags) {
|
||||
// warn!("Video style transfer access is not permitted for user");
|
||||
// return Err(EnqueueStudioGen2Error::NotAuthorized);
|
||||
// return Err(CommonWebError::NotAuthorized);
|
||||
//}
|
||||
|
||||
// ==================== PAID PLAN + PRIORITY ==================== //
|
||||
@@ -205,27 +169,27 @@ pub async fn enqueue_studio_gen2_handler(
|
||||
None => &server_state.redis_rate_limiters.logged_out,
|
||||
Some(ref user) => {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueStudioGen2Error::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
&server_state.redis_rate_limiters.logged_in
|
||||
},
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueStudioGen2Error::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== HANDLE IDEMPOTENCY ==================== //
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(EnqueueStudioGen2Error::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
EnqueueStudioGen2Error::BadInput("invalid idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== LOOK UP MODEL INFO ==================== //
|
||||
@@ -317,9 +281,9 @@ pub async fn enqueue_studio_gen2_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueStudioGen2Error::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueStudioGen2Error::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+16
-56
@@ -3,11 +3,8 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use language_tags::LanguageTag;
|
||||
@@ -15,7 +12,7 @@ use log::{error, info, warn};
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use crate::configs::supported_languages_for_models::get_canonicalized_language_tag_for_model;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::response_success_helpers::simple_json_success;
|
||||
use crate::state::server_state::ServerState;
|
||||
use enums::common::visibility::Visibility;
|
||||
@@ -90,54 +87,17 @@ pub struct EditTtsModelRequest {
|
||||
pub use_default_m_factor: Option<bool>,
|
||||
pub maybe_custom_m_factor: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EditTtsModelError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ModelNotFound,
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for EditTtsModelError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EditTtsModelError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EditTtsModelError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EditTtsModelError::ModelNotFound => StatusCode::NOT_FOUND,
|
||||
EditTtsModelError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EditTtsModelError::BadInput(reason) => reason.to_string(),
|
||||
EditTtsModelError::NotAuthorized=> "unauthorized".to_string(),
|
||||
EditTtsModelError::ModelNotFound => "not found".to_string(),
|
||||
EditTtsModelError::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for EditTtsModelError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn edit_tts_model_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<EditTtsModelPathInfo>,
|
||||
request: Json<EditTtsModelRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, EditTtsModelError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
// NB: Disable if we've migrated to model_weights
|
||||
if server_state.flags.switch_tts_to_model_weights {
|
||||
warn!("Migration to model_weights for tts. Cannot delete old model.");
|
||||
return Err(EditTtsModelError::ServerError);
|
||||
return Err(CommonWebError::server_error_with_message("TTS migrated to model_weights; legacy edit endpoint disabled"));
|
||||
}
|
||||
|
||||
let maybe_user_session = server_state
|
||||
@@ -146,14 +106,14 @@ pub async fn edit_tts_model_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EditTtsModelError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => {
|
||||
warn!("not logged in");
|
||||
return Err(EditTtsModelError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,11 +133,11 @@ pub async fn edit_tts_model_handler(
|
||||
},
|
||||
Ok(None) => {
|
||||
warn!("could not find model");
|
||||
return Err(EditTtsModelError::ModelNotFound);
|
||||
return Err(CommonWebError::NotFound);
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("error looking up model: {:?}", err);
|
||||
return Err(EditTtsModelError::ModelNotFound);
|
||||
return Err(CommonWebError::NotFound);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -187,12 +147,12 @@ pub async fn edit_tts_model_handler(
|
||||
|
||||
if !is_author && !is_mod {
|
||||
warn!("user is not allowed to edit model: {:?}", user_session.user_token);
|
||||
return Err(EditTtsModelError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
if !is_mod {
|
||||
if model_record.is_locked_from_user_modification || model_record.is_locked_from_use {
|
||||
return Err(EditTtsModelError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +173,7 @@ pub async fn edit_tts_model_handler(
|
||||
|
||||
if let Some(payload) = request.title.as_deref() {
|
||||
if contains_slurs(payload) {
|
||||
return Err(EditTtsModelError::BadInput("title contains slurs".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("title contains slurs".to_string()));
|
||||
}
|
||||
|
||||
title = Some(payload.trim().to_string());
|
||||
@@ -221,7 +181,7 @@ pub async fn edit_tts_model_handler(
|
||||
|
||||
if let Some(markdown) = request.description_markdown.as_deref() {
|
||||
if contains_slurs(markdown) {
|
||||
return Err(EditTtsModelError::BadInput("description contains slurs".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("description contains slurs".to_string()));
|
||||
}
|
||||
|
||||
let markdown = markdown.trim().to_string();
|
||||
@@ -243,7 +203,7 @@ pub async fn edit_tts_model_handler(
|
||||
.transpose()
|
||||
.map_err(|e| {
|
||||
error!("Error parsing language tag '{}': {:?}", tag, e);
|
||||
EditTtsModelError::BadInput("bad locale string".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("bad locale string".to_string())
|
||||
})?;
|
||||
|
||||
if let Some(full_tag) = maybe_full_canonical_tag {
|
||||
@@ -262,7 +222,7 @@ pub async fn edit_tts_model_handler(
|
||||
|
||||
if let Some(visibility) = request.creator_set_visibility.as_deref() {
|
||||
creator_set_visibility = Visibility::from_str(visibility)
|
||||
.map_err(|_| EditTtsModelError::BadInput("bad record visibility".to_string()))?;
|
||||
.map_err(|_| CommonWebError::BadInputWithSimpleMessage("bad record visibility".to_string()))?;
|
||||
}
|
||||
|
||||
let text_pipeline_type = request.text_pipeline_type
|
||||
@@ -330,7 +290,7 @@ pub async fn edit_tts_model_handler(
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
warn!("Update W2L model edit DB error: {:?}", err);
|
||||
return Err(EditTtsModelError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -348,7 +308,7 @@ async fn update_mod_details(
|
||||
moderator_user_token: &str,
|
||||
tts_model_token: &str,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> Result<(), EditTtsModelError> {
|
||||
) -> Result<(), CommonWebError> {
|
||||
|
||||
let is_public_listing_approved= request.is_public_listing_approved.unwrap_or(false);
|
||||
let is_locked_from_user_modification = request.is_locked_from_user_modification.unwrap_or(false);
|
||||
@@ -392,7 +352,7 @@ async fn update_mod_details(
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
warn!("Update TTS model (mod details) DB error: {:?}", err);
|
||||
Err(EditTtsModelError::ServerError)
|
||||
Err(CommonWebError::from_error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-49
@@ -3,11 +3,8 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Path;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::{error, warn};
|
||||
@@ -17,7 +14,7 @@ use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use mysql_queries::queries::tts::tts_results::edit_tts_result::{edit_tts_result, CreatorOrModFields, EditTtsResultArgs};
|
||||
use mysql_queries::queries::tts::tts_results::query_tts_result::select_tts_result_by_token;
|
||||
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::response_success_helpers::simple_json_success;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -32,49 +29,12 @@ pub struct EditTtsResultRequest {
|
||||
// ========== Author + Moderator options ==========
|
||||
pub creator_set_visibility: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EditTtsResultError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ResultNotFound,
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for EditTtsResultError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EditTtsResultError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EditTtsResultError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EditTtsResultError::ResultNotFound => StatusCode::NOT_FOUND,
|
||||
EditTtsResultError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EditTtsResultError::BadInput(reason) => reason.to_string(),
|
||||
EditTtsResultError::NotAuthorized=> "unauthorized".to_string(),
|
||||
EditTtsResultError::ResultNotFound => "not found".to_string(),
|
||||
EditTtsResultError::ServerError => "server error".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for EditTtsResultError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn edit_tts_inference_result_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<EditTtsResultPathInfo>,
|
||||
request: web::Json<EditTtsResultRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, EditTtsResultError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -82,14 +42,14 @@ pub async fn edit_tts_inference_result_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EditTtsResultError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => {
|
||||
warn!("not logged in");
|
||||
return Err(EditTtsResultError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -110,9 +70,9 @@ pub async fn edit_tts_inference_result_handler(
|
||||
let inference_result = match inference_result_query_result {
|
||||
Err(e) => {
|
||||
warn!("query error: {:?}", e);
|
||||
return Err(EditTtsResultError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
Ok(None) => return Err(EditTtsResultError::ResultNotFound),
|
||||
Ok(None) => return Err(CommonWebError::NotFound),
|
||||
Ok(Some(inference_result)) => inference_result,
|
||||
};
|
||||
|
||||
@@ -124,7 +84,7 @@ pub async fn edit_tts_inference_result_handler(
|
||||
|
||||
if !is_author && !is_moderator {
|
||||
warn!("user is not allowed to edit result: {:?}", user_session.user_token);
|
||||
return Err(EditTtsResultError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
// Author + Mod fields.
|
||||
@@ -133,7 +93,7 @@ pub async fn edit_tts_inference_result_handler(
|
||||
|
||||
if let Some(visibility) = request.creator_set_visibility.as_deref() {
|
||||
creator_set_visibility = Visibility::from_str(visibility)
|
||||
.map_err(|_| EditTtsResultError::BadInput("bad record visibility".to_string()))?;
|
||||
.map_err(|_| CommonWebError::BadInputWithSimpleMessage("bad record visibility".to_string()))?;
|
||||
}
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
@@ -157,7 +117,7 @@ pub async fn edit_tts_inference_result_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Update TTS result DB error: {:?}", err);
|
||||
EditTtsResultError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
Ok(simple_json_success())
|
||||
|
||||
+14
-54
@@ -3,16 +3,13 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::warn;
|
||||
|
||||
use crate::http_server::validations::validate_model_title::validate_model_title;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::state::server_state::ServerState;
|
||||
use config::bad_urls::is_bad_tts_model_download_url;
|
||||
use enums::by_table::generic_download_jobs::generic_download_type::GenericDownloadType;
|
||||
@@ -69,56 +66,19 @@ pub struct UploadTtsModelSuccessResponse {
|
||||
/// This is a transitional field to tell the frontend how to process the job checking.
|
||||
pub job_type: DownloadJobType,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UploadTtsModelError {
|
||||
BadInput(String),
|
||||
MustBeLoggedIn,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for UploadTtsModelError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
UploadTtsModelError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
UploadTtsModelError::MustBeLoggedIn => StatusCode::UNAUTHORIZED,
|
||||
UploadTtsModelError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
UploadTtsModelError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
UploadTtsModelError::BadInput(reason) => reason.to_string(),
|
||||
UploadTtsModelError::MustBeLoggedIn => "user must be logged in".to_string(),
|
||||
UploadTtsModelError::ServerError => "server error".to_string(),
|
||||
UploadTtsModelError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for UploadTtsModelError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upload_tts_model_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<UploadTtsModelRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, UploadTtsModelError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
if server_state.flags.disable_tts {
|
||||
return Err(UploadTtsModelError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
if let Err(_err) = server_state.redis_rate_limiters.model_upload.rate_limit_request(&http_request).await {
|
||||
return Err(UploadTtsModelError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
let maybe_user_session = server_state
|
||||
@@ -127,23 +87,23 @@ pub async fn upload_tts_model_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
UploadTtsModelError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => {
|
||||
warn!("not logged in");
|
||||
return Err(UploadTtsModelError::MustBeLoggedIn);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(reason) = validate_idempotency_token(&request.idempotency_token) {
|
||||
return Err(UploadTtsModelError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
if let Err(reason) = validate_model_title(&request.title) {
|
||||
return Err(UploadTtsModelError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
@@ -155,11 +115,11 @@ pub async fn upload_tts_model_handler(
|
||||
match is_bad_tts_model_download_url(&download_url) {
|
||||
Ok(false) => {} // Ok case
|
||||
Ok(true) => {
|
||||
return Err(UploadTtsModelError::BadInput("Bad model download URL".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Bad model download URL".to_string()));
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Error parsing url: {:?}", err);
|
||||
return Err(UploadTtsModelError::BadInput("Bad model download URL".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Bad model download URL".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +149,7 @@ pub async fn upload_tts_model_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting new job record: {:?}", err);
|
||||
return Err(UploadTtsModelError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
download_job_type = DownloadJobType::LegacyTts;
|
||||
@@ -211,7 +171,7 @@ pub async fn upload_tts_model_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("New generic download creation DB error (for TTS model): {:?}", err);
|
||||
UploadTtsModelError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
job_token = download_job_token.to_string();
|
||||
@@ -223,7 +183,7 @@ pub async fn upload_tts_model_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("error publishing event: {:?}", e);
|
||||
UploadTtsModelError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let response = UploadTtsModelSuccessResponse {
|
||||
@@ -233,7 +193,7 @@ pub async fn upload_tts_model_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| UploadTtsModelError::ServerError)?;
|
||||
?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+5
-29
@@ -11,6 +11,7 @@ use redis::Commands;
|
||||
use redis_common::redis_keys::RedisKeys;
|
||||
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// For the URL PathInfo
|
||||
@@ -24,41 +25,16 @@ pub struct GetTtsModelUseCountSuccessResponse {
|
||||
pub success: bool,
|
||||
pub count: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum GetTtsModelUseCountError {
|
||||
ServerError,
|
||||
NotFoundError,
|
||||
}
|
||||
|
||||
impl ResponseError for GetTtsModelUseCountError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
GetTtsModelUseCountError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR,
|
||||
GetTtsModelUseCountError::NotFoundError => StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
GetTtsModelUseCountError::ServerError => "server error".to_string(),
|
||||
GetTtsModelUseCountError::NotFoundError => "not found".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_tts_model_use_count_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<GetTtsModelUseCountPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, GetTtsModelUseCountError>
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let mut redis = server_state.redis_pool
|
||||
.get()
|
||||
.map_err(|e| {
|
||||
warn!("redis error: {:?}", e);
|
||||
GetTtsModelUseCountError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let redis_count_key = RedisKeys::tts_model_usage_count(&path.model_token);
|
||||
@@ -66,7 +42,7 @@ pub async fn get_tts_model_use_count_handler(
|
||||
let count : Option<u64> = redis.get(&redis_count_key)
|
||||
.map_err(|e| {
|
||||
warn!("redis error: {:?}", e);
|
||||
GetTtsModelUseCountError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let response = GetTtsModelUseCountSuccessResponse {
|
||||
@@ -75,7 +51,7 @@ pub async fn get_tts_model_use_count_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| GetTtsModelUseCountError::ServerError)?;
|
||||
.map_err(CommonWebError::from_error)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+17
-50
@@ -3,10 +3,7 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Path;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::warn;
|
||||
@@ -14,7 +11,6 @@ use sqlx::MySqlPool;
|
||||
|
||||
use enums::common::visibility::Visibility;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
|
||||
use markdown::simple_markdown_to_html::simple_markdown_to_html;
|
||||
use mysql_queries::queries::users::user_profiles::edit_user_profile_as_account_holder::edit_user_profile_as_account_holder;
|
||||
use mysql_queries::queries::users::user_profiles::edit_user_profile_as_mod::edit_user_profile_as_mod;
|
||||
@@ -25,6 +21,7 @@ use redis_common::redis_cache_keys::RedisCacheKeys;
|
||||
use user_input_common::check_for_slurs::contains_slurs;
|
||||
|
||||
use crate::http_server::session::session_checker::SessionChecker;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::validations::validate_profile_cashapp_username::{normalize_cashapp_username_for_storage, validate_profile_cashapp_username};
|
||||
use crate::http_server::validations::validate_profile_discord_username::validate_profile_discord_username;
|
||||
use crate::http_server::validations::validate_profile_github_username::validate_profile_github_username;
|
||||
@@ -60,37 +57,7 @@ pub struct EditProfileRequest {
|
||||
pub struct EditProfileSuccessResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum EditProfileError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
UserNotFound,
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for EditProfileError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EditProfileError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EditProfileError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EditProfileError::UserNotFound => StatusCode::NOT_FOUND,
|
||||
EditProfileError::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 EditProfileError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn edit_profile_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<EditProfilePathInfo>,
|
||||
@@ -98,27 +65,27 @@ pub async fn edit_profile_handler(
|
||||
mysql_pool: web::Data<MySqlPool>,
|
||||
redis_ttl_cache: web::Data<RedisTtlCache>,
|
||||
session_checker: web::Data<SessionChecker>,
|
||||
) -> Result<HttpResponse, EditProfileError>
|
||||
) -> Result<HttpResponse, CommonWebError>
|
||||
{
|
||||
let maybe_user_session = session_checker
|
||||
.maybe_get_user_session(&http_request, &mysql_pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EditProfileError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => {
|
||||
warn!("not logged in");
|
||||
return Err(EditProfileError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
if user_session.is_banned {
|
||||
// Banned users can't edit anything
|
||||
return Err(EditProfileError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
let user_lookup_result =
|
||||
@@ -127,10 +94,10 @@ pub async fn edit_profile_handler(
|
||||
|
||||
let user_record = match user_lookup_result {
|
||||
Ok(Some(result)) => result,
|
||||
Ok(None) => return Err(EditProfileError::UserNotFound),
|
||||
Ok(None) => return Err(CommonWebError::NotFound),
|
||||
Err(err) => {
|
||||
warn!("lookup error: {:?}", err);
|
||||
return Err(EditProfileError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -146,7 +113,7 @@ pub async fn edit_profile_handler(
|
||||
}
|
||||
|
||||
if !editor_is_original_user && !editor_is_moderator {
|
||||
return Err(EditProfileError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
// Fields to set
|
||||
@@ -165,7 +132,7 @@ pub async fn edit_profile_handler(
|
||||
twitter_username = None;
|
||||
} else {
|
||||
if let Err(reason) = validate_profile_twitter_username(trimmed) {
|
||||
return Err(EditProfileError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
let normalized = normalize_twitter_username_for_storage(trimmed);
|
||||
twitter_username = Some(normalized);
|
||||
@@ -178,7 +145,7 @@ pub async fn edit_profile_handler(
|
||||
twitch_username = None;
|
||||
} else {
|
||||
if let Err(reason) = validate_profile_twitch_username(trimmed) {
|
||||
return Err(EditProfileError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
twitch_username = Some(trimmed);
|
||||
}
|
||||
@@ -190,7 +157,7 @@ pub async fn edit_profile_handler(
|
||||
discord_username = None;
|
||||
} else {
|
||||
if let Err(reason) = validate_profile_discord_username(trimmed) {
|
||||
return Err(EditProfileError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
discord_username = Some(trimmed);
|
||||
}
|
||||
@@ -202,7 +169,7 @@ pub async fn edit_profile_handler(
|
||||
github_username = None;
|
||||
} else {
|
||||
if let Err(reason) = validate_profile_github_username(trimmed) {
|
||||
return Err(EditProfileError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
github_username = Some(trimmed);
|
||||
}
|
||||
@@ -214,7 +181,7 @@ pub async fn edit_profile_handler(
|
||||
cashapp_username = None;
|
||||
} else {
|
||||
if let Err(reason) = validate_profile_cashapp_username(trimmed) {
|
||||
return Err(EditProfileError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
let normalized = normalize_cashapp_username_for_storage(trimmed);
|
||||
cashapp_username = Some(normalized);
|
||||
@@ -227,7 +194,7 @@ pub async fn edit_profile_handler(
|
||||
website_url = None;
|
||||
} else {
|
||||
if let Err(reason) = validate_profile_website_url(trimmed) {
|
||||
return Err(EditProfileError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
website_url = Some(trimmed);
|
||||
}
|
||||
@@ -235,7 +202,7 @@ pub async fn edit_profile_handler(
|
||||
|
||||
if let Some(markdown) = request.profile_markdown.as_deref() {
|
||||
if contains_slurs(markdown) {
|
||||
return Err(EditProfileError::BadInput("profile contains slurs".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("profile contains slurs".to_string()));
|
||||
}
|
||||
|
||||
let markdown = markdown.trim().to_string();
|
||||
@@ -294,7 +261,7 @@ pub async fn edit_profile_handler(
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
warn!("Profile edit DB error: {:?}", err);
|
||||
return Err(EditProfileError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -319,7 +286,7 @@ pub async fn edit_profile_handler(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| EditProfileError::BadInput("".to_string()))?;
|
||||
.map_err(|_e| CommonWebError::BadInputWithSimpleMessage("".to_string()))?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+16
-59
@@ -3,13 +3,10 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::{info, warn};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
@@ -30,10 +27,10 @@ use tokens::tokens::users::UserToken;
|
||||
use tts_common::priority::{FAKEYOU_DEFAULT_VALID_API_TOKEN_PRIORITY_LEVEL, FAKEYOU_INVESTOR_PRIORITY_LEVEL};
|
||||
|
||||
use crate::configs::app_startup::username_set::UsernameSet;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::deprecated_endpoints::investor_demo::demo_cookie::request_has_demo_cookie;
|
||||
use crate::http_server::session::lookup::user_session_extended::UserSessionExtended;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
// TODO: Temporary for investor demo
|
||||
@@ -62,57 +59,17 @@ pub struct InferSeedVcSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum InferSeedVcError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl ResponseError for InferSeedVcError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
InferSeedVcError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
InferSeedVcError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
InferSeedVcError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
InferSeedVcError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
InferSeedVcError::NotFound => StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
InferSeedVcError::BadInput(reason) => reason.to_string(),
|
||||
InferSeedVcError::NotAuthorized => "unauthorized".to_string(),
|
||||
InferSeedVcError::ServerError => "server error".to_string(),
|
||||
InferSeedVcError::RateLimited => "rate limited".to_string(),
|
||||
InferSeedVcError::NotFound => "not found".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for InferSeedVcError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Voice Conversion",
|
||||
path = "/v1/voice_conversion/seed_vc_inference",
|
||||
responses(
|
||||
(status = 200, description = "Success response", body = InferSeedVcSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = InferSeedVcError),
|
||||
(status = 401, description = "Not authorized", body = InferSeedVcError),
|
||||
(status = 429, description = "Rate limited", body = InferSeedVcError),
|
||||
(status = 500, description = "Server error", body = InferSeedVcError)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(("request" = InferSeedVcRequest, description = "Payload for Request"))
|
||||
)]
|
||||
@@ -120,10 +77,10 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<InferSeedVcRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<InferSeedVcSuccessResponse>, InferSeedVcError> {
|
||||
) -> Result<Json<InferSeedVcSuccessResponse>, CommonWebError> {
|
||||
|
||||
if server_state.flags.disable_voice_conversion {
|
||||
return Err(InferSeedVcError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
let mut is_from_api = false;
|
||||
@@ -137,7 +94,7 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
InferSeedVcError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -148,7 +105,7 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
InferSeedVcError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
if let Some(user_session) = maybe_user_session.as_ref() {
|
||||
@@ -215,7 +172,7 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
warn!("User is not authorized to use Seed-VC because they are banned.");
|
||||
return Err(InferSeedVcError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +206,7 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(InferSeedVcError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +240,7 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
} else if source_media_token.starts_with(MediaFileToken::token_prefix()) {
|
||||
InferenceInputSourceTokenType::MediaFile
|
||||
} else {
|
||||
return Err(InferSeedVcError::BadInput(
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"input token is not a media_upload or media_file token".to_string()));
|
||||
};
|
||||
|
||||
@@ -295,7 +252,7 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
} else if reference_media_token.starts_with(MediaFileToken::token_prefix()) {
|
||||
InferenceInputSourceTokenType::MediaFile
|
||||
} else {
|
||||
return Err(InferSeedVcError::BadInput(
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"reference token is not a media_upload or media_file token".to_string()));
|
||||
};
|
||||
|
||||
@@ -337,9 +294,9 @@ pub async fn enqueue_infer_seed_vc_handler(
|
||||
Err(err) => {
|
||||
warn!("New (generic) seed-vc inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(InferSeedVcError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(InferSeedVcError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+24
-64
@@ -3,13 +3,10 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::{error, info, warn};
|
||||
use redis::Commands;
|
||||
use sqlx::pool::PoolConnection;
|
||||
@@ -35,8 +32,8 @@ use tokens::tokens::users::UserToken;
|
||||
use tts_common::priority::FAKEYOU_INVESTOR_PRIORITY_LEVEL;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::deprecated_endpoints::investor_demo::demo_cookie::request_has_demo_cookie;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::memory_cache::model_token_to_info_cache::{ModelInfoForInferenceJob, ModelTokenToInfoCache};
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -97,54 +94,17 @@ pub struct EnqueueVoiceConversionInferenceSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum EnqueueVoiceConversionInferenceError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueVoiceConversionInferenceError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueVoiceConversionInferenceError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueVoiceConversionInferenceError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueVoiceConversionInferenceError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueVoiceConversionInferenceError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueVoiceConversionInferenceError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueVoiceConversionInferenceError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueVoiceConversionInferenceError::ServerError => "server error".to_string(),
|
||||
EnqueueVoiceConversionInferenceError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for EnqueueVoiceConversionInferenceError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Voice Conversion",
|
||||
path = "/v1/voice_conversion/inference",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = EnqueueVoiceConversionInferenceSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = EnqueueVoiceConversionInferenceError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueVoiceConversionInferenceError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueVoiceConversionInferenceError),
|
||||
(status = 500, description = "Server error", body = EnqueueVoiceConversionInferenceError),
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError),
|
||||
),
|
||||
params(
|
||||
("request" = EnqueueVoiceConversionInferenceRequest, description = "Payload for Request"),
|
||||
@@ -154,11 +114,11 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EnqueueVoiceConversionInferenceRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<EnqueueVoiceConversionInferenceSuccessResponse>, EnqueueVoiceConversionInferenceError>
|
||||
) -> Result<Json<EnqueueVoiceConversionInferenceSuccessResponse>, CommonWebError>
|
||||
{
|
||||
|
||||
if server_state.flags.disable_voice_conversion {
|
||||
return Err(EnqueueVoiceConversionInferenceError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
let mut maybe_user_token : Option<UserToken> = None;
|
||||
@@ -170,7 +130,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== AVT TOKEN ==================== //
|
||||
@@ -185,7 +145,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
if let Some(user_session) = maybe_user_session.as_ref() {
|
||||
@@ -239,7 +199,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueVoiceConversionInferenceError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +217,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
}
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueVoiceConversionInferenceError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +242,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
} else if media_token.starts_with(MediaFileToken::token_prefix()) {
|
||||
InferenceInputSourceTokenType::MediaFile
|
||||
} else {
|
||||
return Err(EnqueueVoiceConversionInferenceError::BadInput(
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"input token is not a media_upload or media_file token".to_string()));
|
||||
};
|
||||
|
||||
@@ -292,7 +252,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
.get()
|
||||
.map_err(|e| {
|
||||
error!("redis error: {:?}", e);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let redis_count_key = RedisKeys::web_vc_model_usage_count(&model_token);
|
||||
@@ -300,7 +260,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
redis.incr::<_, _, ()>(&redis_count_key, 1)
|
||||
.map_err(|e| {
|
||||
warn!("redis error: {:?}", e);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
@@ -340,7 +300,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
_ => {
|
||||
// In theory, this shouldn't catch anything.
|
||||
error!("wrong model type for voice conversion: {:?}", model_inference_info.job_model_type);
|
||||
return Err(EnqueueVoiceConversionInferenceError::ServerError)
|
||||
return Err(CommonWebError::server_error_with_message("wrong model type for voice conversion"))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -383,9 +343,9 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueVoiceConversionInferenceError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueVoiceConversionInferenceError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -395,7 +355,7 @@ pub async fn enqueue_voice_conversion_inference_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("error publishing event: {:?}", e);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
Ok(Json(EnqueueVoiceConversionInferenceSuccessResponse {
|
||||
@@ -408,12 +368,12 @@ async fn lookup_model_info(
|
||||
model_token: &str,
|
||||
cache: &ModelTokenToInfoCache,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> Result<ModelInfoForInferenceJob, EnqueueVoiceConversionInferenceError> {
|
||||
) -> Result<ModelInfoForInferenceJob, CommonWebError> {
|
||||
let maybe_model_inference_info = cache
|
||||
.get_info(model_token)
|
||||
.map_err(|err| {
|
||||
error!("in-memory cache error: {:?}", err);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
if let Some(inference_info) = maybe_model_inference_info {
|
||||
@@ -425,7 +385,7 @@ async fn lookup_model_info(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("model lookup error: {:?}", err);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let model_info = match result {
|
||||
@@ -449,14 +409,14 @@ async fn lookup_model_info(
|
||||
}
|
||||
_ => {
|
||||
error!("wrong model type for inference: {:?}", model_info);
|
||||
return Err(EnqueueVoiceConversionInferenceError::BadInput("wrong model type for inference".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("wrong model type for inference".to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
cache.insert_one(model_token, &inference_info)
|
||||
.map_err(|err| {
|
||||
error!("cache insertion error: {:?}", err);
|
||||
EnqueueVoiceConversionInferenceError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
Ok(inference_info)
|
||||
|
||||
+13
-53
@@ -2,11 +2,8 @@
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::warn;
|
||||
use serde::Deserialize;
|
||||
@@ -34,7 +31,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Debug requests can get routed to special "debug-only" workers, which can
|
||||
@@ -58,43 +55,6 @@ pub struct EnqueueTTSRequestSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug,ToSchema)]
|
||||
pub enum EnqueueTTSRequestError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueTTSRequestError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueTTSRequestError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueTTSRequestError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueTTSRequestError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueTTSRequestError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueTTSRequestError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueTTSRequestError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueTTSRequestError::ServerError => "server error".to_string(),
|
||||
EnqueueTTSRequestError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EnqueueTTSRequestError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for enqueuing a TTS request
|
||||
// Reference enqueue_infer_tts_handler.rs for checks: rate limiting / user sessions
|
||||
// insert generic inference job.rs
|
||||
@@ -105,10 +65,10 @@ impl std::fmt::Display for EnqueueTTSRequestError {
|
||||
path = "/v1/voice_designer/inference/enqueue_tts",
|
||||
responses(
|
||||
(status = 200, description = "Enqueue TTS generically", body = EnqueueTTSRequestSuccessResponse),
|
||||
(status = 400, description = "Bad input", body = EnqueueTTSRequestError),
|
||||
(status = 401, description = "Not authorized", body = EnqueueTTSRequestError),
|
||||
(status = 429, description = "Rate limited", body = EnqueueTTSRequestError),
|
||||
(status = 500, description = "Server error", body = EnqueueTTSRequestError)
|
||||
(status = 400, description = "Bad input", body = CommonWebError),
|
||||
(status = 401, description = "Not authorized", body = CommonWebError),
|
||||
(status = 429, description = "Rate limited", body = CommonWebError),
|
||||
(status = 500, description = "Server error", body = CommonWebError)
|
||||
),
|
||||
params(
|
||||
("request" = EnqueueTTSRequest, description = "Payload for TTS Request")
|
||||
@@ -118,13 +78,13 @@ pub async fn enqueue_tts_request(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<EnqueueTTSRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, EnqueueTTSRequestError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let mut maybe_user_token: Option<UserToken> = None;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueTTSRequestError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -133,7 +93,7 @@ pub async fn enqueue_tts_request(
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, &mut mysql_connection).await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueTTSRequestError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
if let Some(user_session) = maybe_user_session.as_ref() {
|
||||
@@ -161,7 +121,7 @@ pub async fn enqueue_tts_request(
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueTTSRequestError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +133,7 @@ pub async fn enqueue_tts_request(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueTTSRequestError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// Get up IP address
|
||||
@@ -223,9 +183,9 @@ pub async fn enqueue_tts_request(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueTTSRequestError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueTTSRequestError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -234,7 +194,7 @@ pub async fn enqueue_tts_request(
|
||||
inference_job_token: job_token,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response).map_err(|_e| EnqueueTTSRequestError::ServerError)?;
|
||||
let body = serde_json::to_string(&response)?;
|
||||
|
||||
// Error handling 101 rust result type returned like so.
|
||||
Ok(HttpResponse::Ok().content_type("application/json").body(body))
|
||||
|
||||
+22
-53
@@ -23,6 +23,7 @@ use tokens::tokens::zs_voice_dataset_samples::ZsVoiceDatasetSampleToken;
|
||||
use tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken;
|
||||
|
||||
use crate::http_server::deprecated_endpoints::media_uploads::common::drain_multipart_request::{drain_multipart_request, MediaSource};
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -32,39 +33,7 @@ pub struct UploadSampleResponse {
|
||||
pub sample_token: ZsVoiceDatasetSampleToken,
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum UploadSampleError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
MustBeLoggedIn,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for UploadSampleError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
UploadSampleError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
UploadSampleError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
UploadSampleError::MustBeLoggedIn => StatusCode::UNAUTHORIZED,
|
||||
UploadSampleError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
UploadSampleError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl std::fmt::Display for UploadSampleError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
static ALLOWED_AUDIO_MIME_TYPES : Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
||||
HashSet::from([
|
||||
"audio/aac",
|
||||
@@ -87,14 +56,14 @@ pub async fn upload_zs_sample_handler(
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
mut multipart_payload: Multipart,
|
||||
) -> Result<HttpResponse, UploadSampleError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("MySql pool error: {:?}", err);
|
||||
UploadSampleError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== READ SESSION ==================== //
|
||||
@@ -105,14 +74,14 @@ pub async fn upload_zs_sample_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Session checker error: {:?}", e);
|
||||
UploadSampleError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
// ==================== BANNED USERS ==================== //
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.is_banned {
|
||||
return Err(UploadSampleError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,11 +93,11 @@ pub async fn upload_zs_sample_handler(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(UploadSampleError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
if let Err(_err) = server_state.redis_rate_limiters.model_upload.rate_limit_request(&http_request).await {
|
||||
return Err(UploadSampleError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
// ==================== READ MULTIPART REQUEST ==================== //
|
||||
@@ -137,11 +106,11 @@ pub async fn upload_zs_sample_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
// TODO: Error handling could be nicer.
|
||||
UploadSampleError::BadInput("bad request".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("bad request".to_string())
|
||||
})?;
|
||||
|
||||
let uuid_idempotency_token = upload_sample_request.uuid_idempotency_token
|
||||
.ok_or(UploadSampleError::BadInput("no uuid".to_string()))?;
|
||||
.ok_or(CommonWebError::BadInputWithSimpleMessage("no uuid".to_string()))?;
|
||||
|
||||
let maybe_existing_upload =
|
||||
get_dataset_sample_by_uuid_with_connection(&uuid_idempotency_token, &mut mysql_connection)
|
||||
@@ -150,7 +119,7 @@ pub async fn upload_zs_sample_handler(
|
||||
match maybe_existing_upload {
|
||||
Err(err) => {
|
||||
error!("Error checking for previous upload: {:?}", err);
|
||||
return Err(UploadSampleError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
Ok(Some(previous_record)) => {
|
||||
return success_response(
|
||||
@@ -164,7 +133,7 @@ pub async fn upload_zs_sample_handler(
|
||||
}
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&uuid_idempotency_token) {
|
||||
return Err(UploadSampleError::BadInput(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
let creator_set_visibility = maybe_user_session
|
||||
@@ -191,19 +160,19 @@ pub async fn upload_zs_sample_handler(
|
||||
.flatten();
|
||||
|
||||
let dataset_token = match upload_sample_request.dataset_token {
|
||||
None => return Err(UploadSampleError::BadInput("missing dataset token".to_string())),
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("missing dataset token".to_string())),
|
||||
Some(token) => ZsVoiceDatasetToken::new_from_str(&token),
|
||||
};
|
||||
|
||||
let bytes = match upload_sample_request.file_bytes {
|
||||
None => return Err(UploadSampleError::BadInput("missing file contents".to_string())),
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("missing file contents".to_string())),
|
||||
Some(bytes) => bytes,
|
||||
};
|
||||
|
||||
let hash = sha256_hash_bytes(&bytes)
|
||||
.map_err(|io_error| {
|
||||
error!("Problem hashing bytes: {:?}", io_error);
|
||||
UploadSampleError::ServerError
|
||||
CommonWebError::from_anyhow_error(io_error)
|
||||
})?;
|
||||
|
||||
let file_size_bytes = bytes.len();
|
||||
@@ -221,7 +190,7 @@ pub async fn upload_zs_sample_handler(
|
||||
.filter(|c| c.is_ascii())
|
||||
.filter(|c| c.is_alphanumeric() || *c == '/')
|
||||
.collect::<String>();
|
||||
return Err(UploadSampleError::BadInput(format!("unpermitted mime type: {}", &filtered_mimetype)));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(format!("unpermitted mime type: {}", &filtered_mimetype)));
|
||||
}
|
||||
|
||||
// NB: .aiff (audio/aiff) isn't supported by Symphonia:
|
||||
@@ -285,7 +254,7 @@ pub async fn upload_zs_sample_handler(
|
||||
None
|
||||
).map_err(|e| {
|
||||
warn!("file decoding error: {:?}", e);
|
||||
UploadSampleError::BadInput("could not decode file".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("could not decode file".to_string())
|
||||
})?;
|
||||
|
||||
maybe_duration_millis = basic_info.duration_millis;
|
||||
@@ -297,7 +266,7 @@ pub async fn upload_zs_sample_handler(
|
||||
Some(m) => m,
|
||||
None => {
|
||||
warn!("Invalid mimetype: {:?}", maybe_mimetype);
|
||||
return Err(UploadSampleError::BadInput(format!("unknown mimetype: {:?}", maybe_mimetype)))
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(format!("unknown mimetype: {:?}", maybe_mimetype)))
|
||||
},
|
||||
};
|
||||
|
||||
@@ -312,7 +281,7 @@ pub async fn upload_zs_sample_handler(
|
||||
Some(m) => m,
|
||||
None => {
|
||||
warn!("Missing mimetype!");
|
||||
return Err(UploadSampleError::BadInput("Missing mimetype".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Missing mimetype".to_string()));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -335,7 +304,7 @@ pub async fn upload_zs_sample_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Upload media bytes to bucket error: {:?}", e);
|
||||
UploadSampleError::ServerError
|
||||
CommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let (dataset_sample_token, media_file_token, _record_id) = insert_dataset_sample_and_media_file(InsertDatasetSampleAndMediaFileArgs {
|
||||
@@ -362,7 +331,7 @@ pub async fn upload_zs_sample_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("New generic download creation DB error: {:?}", err);
|
||||
UploadSampleError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
info!("new media file token: {:?} dataset token: {:?}", media_file_token, dataset_sample_token);
|
||||
@@ -376,7 +345,7 @@ pub async fn upload_zs_sample_handler(
|
||||
fn success_response(
|
||||
sample_token: ZsVoiceDatasetSampleToken,
|
||||
media_file_token: MediaFileToken
|
||||
) -> Result<HttpResponse, UploadSampleError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let response = UploadSampleResponse {
|
||||
success: true,
|
||||
@@ -385,7 +354,7 @@ fn success_response(
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| UploadSampleError::ServerError)?;
|
||||
.map_err(CommonWebError::from_error)?;
|
||||
|
||||
return Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
|
||||
+9
-45
@@ -32,6 +32,7 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -46,43 +47,6 @@ pub struct EnqueueCreateVoiceRequestSuccessResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EnqueueCreateVoiceRequestError {
|
||||
BadInput(String),
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl ResponseError for EnqueueCreateVoiceRequestError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
EnqueueCreateVoiceRequestError::BadInput(_) => StatusCode::BAD_REQUEST,
|
||||
EnqueueCreateVoiceRequestError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
EnqueueCreateVoiceRequestError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EnqueueCreateVoiceRequestError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let error_reason = match self {
|
||||
EnqueueCreateVoiceRequestError::BadInput(reason) => reason.to_string(),
|
||||
EnqueueCreateVoiceRequestError::NotAuthorized => "unauthorized".to_string(),
|
||||
EnqueueCreateVoiceRequestError::ServerError => "server error".to_string(),
|
||||
EnqueueCreateVoiceRequestError::RateLimited => "rate limited".to_string(),
|
||||
};
|
||||
|
||||
to_simple_json_error(&error_reason, self.status_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for EnqueueCreateVoiceRequestError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug requests can get routed to special "debug-only" workers, which can
|
||||
/// be used to trial new code, run debugging, etc.
|
||||
const DEBUG_HEADER_NAME: &str = "enable-debug-mode";
|
||||
@@ -96,7 +60,7 @@ pub async fn create_voice_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<EnqueueCreateVoiceRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, EnqueueCreateVoiceRequestError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
// voice data set token ... zsd_wb08asm0m4a4cz42wwqked6tfg6e e.g
|
||||
|
||||
// need this for the job
|
||||
@@ -107,7 +71,7 @@ pub async fn create_voice_handler(
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
|
||||
warn!("MySql pool error: {:?}", err);
|
||||
EnqueueCreateVoiceRequestError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
@@ -116,7 +80,7 @@ pub async fn create_voice_handler(
|
||||
.maybe_get_user_session_extended_from_connection(&http_request, &mut mysql_connection).await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
EnqueueCreateVoiceRequestError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
if let Some(user_session) = maybe_user_session.as_ref() {
|
||||
@@ -144,7 +108,7 @@ pub async fn create_voice_handler(
|
||||
|
||||
if let Some(ref user) = maybe_user_session {
|
||||
if user.role.is_banned {
|
||||
return Err(EnqueueCreateVoiceRequestError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +120,7 @@ pub async fn create_voice_handler(
|
||||
};
|
||||
|
||||
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
|
||||
return Err(EnqueueCreateVoiceRequestError::RateLimited);
|
||||
return Err(CommonWebError::TooManyRequests);
|
||||
}
|
||||
|
||||
info!("Received payload for voice creation.");
|
||||
@@ -208,9 +172,9 @@ pub async fn create_voice_handler(
|
||||
Err(err) => {
|
||||
warn!("New generic inference job creation DB error: {:?}", err);
|
||||
if err.had_duplicate_idempotency_token() {
|
||||
return Err(EnqueueCreateVoiceRequestError::BadInput("Duplicate idempotency token".to_string()));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Duplicate idempotency token".to_string()));
|
||||
}
|
||||
return Err(EnqueueCreateVoiceRequestError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -222,7 +186,7 @@ pub async fn create_voice_handler(
|
||||
|
||||
let body = serde_json
|
||||
::to_string(&response)
|
||||
.map_err(|_e| EnqueueCreateVoiceRequestError::ServerError)?;
|
||||
?;
|
||||
// Error handling 101 rust result type returned like so.
|
||||
Ok(HttpResponse::Ok().content_type("application/json").body(body))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user