mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-30 05:22:51 -04:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a4c3593ee | ||
|
|
a2efe1eeb3 | ||
|
|
2d7a24315f | ||
|
|
6f9ea92106 | ||
|
|
f3afc4b046 | ||
|
|
b91d75b6cc | ||
|
|
417e915882 | ||
|
|
6e8a7bf817 | ||
|
|
110f9327d8 | ||
|
|
19d0813f02 | ||
|
|
06cc380d74 | ||
|
|
d25b206cae | ||
|
|
96b6150051 | ||
|
|
1d6d0ec1c3 |
Generated
+1
-1
@@ -4865,7 +4865,6 @@ version = "0.0.1"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"coarsetime",
|
||||
"errors",
|
||||
"jwt-simple",
|
||||
"reqwest 0.12.23",
|
||||
"serde",
|
||||
@@ -11543,6 +11542,7 @@ dependencies = [
|
||||
"elasticsearch",
|
||||
"elasticsearch_schema",
|
||||
"enums",
|
||||
"erased-serde",
|
||||
"error_utils",
|
||||
"errors",
|
||||
"fal_client",
|
||||
|
||||
@@ -334,6 +334,7 @@ regex = "1.12.3"
|
||||
reqwest = { version = "0.12.23", default-features = false, features = [ "charset", "http2", "system-proxy", "json", "cookies", "rustls-tls", "gzip" ] }
|
||||
reqwest-websocket = { version = "0.5.0" }
|
||||
rust-s3 = { version = "0.36.0-beta.2", default-features = false, features = ["fail-on-err", "tags", "tokio-rustls-tls"] }
|
||||
erased-serde = "0.4"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_derive = "1.0.228"
|
||||
serde_json = "1.0.149"
|
||||
|
||||
@@ -21,7 +21,6 @@ path = "src/lib.rs"
|
||||
workspace-build-acceleration.workspace = true
|
||||
|
||||
# Internal
|
||||
errors.workspace = true
|
||||
|
||||
# External
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# Google Sign In
|
||||
|
||||
https://developers.google.com/identity/sign-in/web/sign-in
|
||||
|
||||
This has a link to the management console.
|
||||
|
||||
## Frontend Setup
|
||||
|
||||
```html
|
||||
<!-- Load Library -->
|
||||
<script src="https://apis.google.com/js/platform.js" async defer></script>
|
||||
|
||||
<!-- Specify Client ID-->
|
||||
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID.apps.googleusercontent.com">
|
||||
```
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
use crate::certs::jwk_to_public_key::jwk_to_public_key;
|
||||
use crate::certs::key_map::KeyMap;
|
||||
use errors::{anyhow, AnyhowResult};
|
||||
use crate::error::google_sign_in_error::GoogleSignInError;
|
||||
|
||||
/// https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
|
||||
/// "These keys are regularly rotated; examine the Cache-Control header in
|
||||
/// the response to determine when you should retrieve them again."
|
||||
const GOOGLE_CERTS_URL : &str = "https://www.googleapis.com/oauth2/v3/certs";
|
||||
|
||||
pub async fn download_cert_key_set() -> AnyhowResult<KeyMap> {
|
||||
pub async fn download_cert_key_set() -> Result<KeyMap, GoogleSignInError> {
|
||||
let certs = download_certs().await?;
|
||||
let key_map = jwk_to_public_key(&certs)?;
|
||||
Ok(key_map)
|
||||
}
|
||||
|
||||
pub async fn download_certs() -> AnyhowResult<String> {
|
||||
let response = reqwest::get(GOOGLE_CERTS_URL).await?;
|
||||
pub async fn download_certs() -> Result<String, GoogleSignInError> {
|
||||
let response = reqwest::get(GOOGLE_CERTS_URL)
|
||||
.await
|
||||
.map_err(GoogleSignInError::CertDownloadHttpError)?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(anyhow!("Failed to download Google certs: {:?}", response.status()));
|
||||
return Err(GoogleSignInError::CertDownloadBadStatus(response.status()));
|
||||
}
|
||||
|
||||
let body = response.text().await?;
|
||||
let body = response.text()
|
||||
.await
|
||||
.map_err(GoogleSignInError::CertDownloadHttpError)?;
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::certs::key_map::KeyMap;
|
||||
use errors::{anyhow, AnyhowResult};
|
||||
use crate::error::google_sign_in_error::GoogleSignInError;
|
||||
use jwt_simple::prelude::RS256PublicKey;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
@@ -7,10 +7,11 @@ use serde_derive::Deserialize;
|
||||
/// Grab Google's keys from here: https://www.googleapis.com/oauth2/v3/certs
|
||||
/// These are updated frequently throughout the day and must be re-fetched.
|
||||
/// NB: This implementation is adapted from the crate `signin`.
|
||||
pub fn jwk_to_public_key(json_web_key: &str) -> AnyhowResult<KeyMap> {
|
||||
pub fn jwk_to_public_key(json_web_key: &str) -> Result<KeyMap, GoogleSignInError> {
|
||||
let mut key_map = KeyMap::new();
|
||||
|
||||
let json_keys: JsonKeys = serde_json::from_str(json_web_key)?;
|
||||
let json_keys: JsonKeys = serde_json::from_str(json_web_key)
|
||||
.map_err(GoogleSignInError::JwkJsonParseFailed)?;
|
||||
|
||||
for key in json_keys.keys {
|
||||
if key.use_ != "sig" {
|
||||
@@ -18,11 +19,14 @@ pub fn jwk_to_public_key(json_web_key: &str) -> AnyhowResult<KeyMap> {
|
||||
}
|
||||
match key.alg.as_ref() {
|
||||
"RS256" => {
|
||||
let n_decoded = base64_decode_url(&key.n)?;
|
||||
let e_decoded = base64_decode_url(&key.e)?;
|
||||
let n_decoded = base64_decode_url(&key.n)
|
||||
.map_err(GoogleSignInError::JwkBase64DecodeFailed)?;
|
||||
let e_decoded = base64_decode_url(&key.e)
|
||||
.map_err(GoogleSignInError::JwkBase64DecodeFailed)?;
|
||||
|
||||
let public_key = RS256PublicKey::from_components(&n_decoded, &e_decoded)?
|
||||
.with_key_id(&key.kid);
|
||||
let public_key = RS256PublicKey::from_components(&n_decoded, &e_decoded)
|
||||
.map_err(|err| GoogleSignInError::JwkRsaKeyConstructionFailed(format!("{}", err)))?
|
||||
.with_key_id(&key.kid);
|
||||
|
||||
key_map.insert(key.kid, public_key);
|
||||
}
|
||||
@@ -31,7 +35,7 @@ pub fn jwk_to_public_key(json_web_key: &str) -> AnyhowResult<KeyMap> {
|
||||
}
|
||||
|
||||
if key_map.is_empty() {
|
||||
return Err(anyhow!("No keys found in JWK"));
|
||||
return Err(GoogleSignInError::JwkNoKeysFound);
|
||||
}
|
||||
|
||||
Ok(key_map)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::claims::google_custom_claims::GoogleCustomClaims;
|
||||
use errors::{anyhow, AnyhowResult};
|
||||
use crate::error::google_sign_in_error::GoogleSignInError;
|
||||
use jwt_simple::claims::{Audiences, JWTClaims};
|
||||
|
||||
pub struct Claims {
|
||||
@@ -37,11 +37,11 @@ impl Claims {
|
||||
|
||||
/// Determine if the claim audience is as expected.
|
||||
/// This is necessary so third parties don't send claims signed on their behalf by Google.
|
||||
pub fn audience_matches(&self, audience: &str) -> AnyhowResult<bool> {
|
||||
pub fn audience_matches(&self, audience: &str) -> Result<bool, GoogleSignInError> {
|
||||
match self.claims.audiences.as_ref() {
|
||||
Some(Audiences::AsString(claim_audience)) => Ok(claim_audience.eq(audience)),
|
||||
Some(Audiences::AsSet(audiences)) => Ok(audiences.contains(audience)),
|
||||
_ => Err(anyhow!("Invalid audience type")),
|
||||
_ => Err(GoogleSignInError::AudienceMissing),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::certs::key_map::KeyMap;
|
||||
use crate::claims::claims::Claims;
|
||||
use crate::claims::google_custom_claims::GoogleCustomClaims;
|
||||
use crate::errors::VerifyError;
|
||||
use crate::error::google_sign_in_error::GoogleSignInError;
|
||||
use crate::jwt::decode_jwt_header::decode_jwt_header;
|
||||
use errors::anyhow;
|
||||
use jwt_simple::algorithms::RS256PublicKey;
|
||||
use jwt_simple::algorithms::RSAPublicKeyLike;
|
||||
use jwt_simple::common::VerificationOptions;
|
||||
@@ -12,16 +11,15 @@ use std::collections::HashSet;
|
||||
|
||||
/// Decode a Google Sign In JWT.
|
||||
/// Verification options can be supplied to increase clock skew tolerance, etc.
|
||||
pub fn decode_and_verify_token_claims(keys: &KeyMap, token: &str, mut options: Option<VerificationOptions>) -> Result<Claims, VerifyError> {
|
||||
let header = decode_jwt_header(token)
|
||||
.map_err(|err| VerifyError::JwtDecodeError { source: err })?;
|
||||
pub fn decode_and_verify_token_claims(keys: &KeyMap, token: &str, mut options: Option<VerificationOptions>) -> Result<Claims, GoogleSignInError> {
|
||||
let header = decode_jwt_header(token)?;
|
||||
|
||||
let key_id = header.kid.as_deref()
|
||||
.or_else(|| keys.keys().next().map(|k|k.as_str()))
|
||||
.ok_or_else(|| anyhow!("No key ID found"))?;
|
||||
.ok_or(GoogleSignInError::JwtNoKeyId)?;
|
||||
|
||||
let key = keys.get(key_id)
|
||||
.ok_or_else(|| VerifyError::JwtKeyMissing { requested_key: key_id.to_string() })?;
|
||||
.ok_or_else(|| GoogleSignInError::JwtKeyMissing { requested_key: key_id.to_string() })?;
|
||||
|
||||
if options.is_none() {
|
||||
options = Some(VerificationOptions {
|
||||
@@ -36,7 +34,7 @@ pub fn decode_and_verify_token_claims(keys: &KeyMap, token: &str, mut options: O
|
||||
decode_and_verify_token_claims_with_key(key, token, options)
|
||||
}
|
||||
|
||||
pub fn decode_and_verify_token_claims_with_key(key: &RS256PublicKey, token: &str, options: Option<VerificationOptions>) -> Result<Claims, VerifyError> {
|
||||
pub fn decode_and_verify_token_claims_with_key(key: &RS256PublicKey, token: &str, options: Option<VerificationOptions>) -> Result<Claims, GoogleSignInError> {
|
||||
let result = key.verify_token::<GoogleCustomClaims>(token, options);
|
||||
|
||||
let claims = match result {
|
||||
@@ -44,22 +42,22 @@ pub fn decode_and_verify_token_claims_with_key(key: &RS256PublicKey, token: &str
|
||||
Err(err) => {
|
||||
if let Some(inner_error) = err.downcast_ref::<JWTError>() {
|
||||
match inner_error {
|
||||
JWTError::RequiredAudienceMismatch => return Err(VerifyError::JwtInvalidAudience),
|
||||
JWTError::RequiredAudienceMissing => return Err(VerifyError::JwtInvalidAudience),
|
||||
JWTError::RequiredIssuerMismatch => return Err(VerifyError::JwtInvalidIssuer),
|
||||
JWTError::RequiredIssuerMissing => return Err(VerifyError::JwtInvalidIssuer),
|
||||
JWTError::TokenHasExpired => return Err(VerifyError::JwtExpired),
|
||||
JWTError::RequiredAudienceMismatch => return Err(GoogleSignInError::JwtInvalidAudience),
|
||||
JWTError::RequiredAudienceMissing => return Err(GoogleSignInError::JwtInvalidAudience),
|
||||
JWTError::RequiredIssuerMismatch => return Err(GoogleSignInError::JwtInvalidIssuer),
|
||||
JWTError::RequiredIssuerMissing => return Err(GoogleSignInError::JwtInvalidIssuer),
|
||||
JWTError::TokenHasExpired => return Err(GoogleSignInError::JwtExpired),
|
||||
_ => {}, // Fall-through
|
||||
}
|
||||
}
|
||||
return Err(VerifyError::AnyhowError(err));
|
||||
return Err(GoogleSignInError::JwtVerifyFailed(format!("{}", err)));
|
||||
}
|
||||
};
|
||||
|
||||
match claims.issuer.as_deref() {
|
||||
Some("https://accounts.google.com" | "accounts.google.com") => {} // Permitted
|
||||
_ => {
|
||||
return Err(VerifyError::JwtInvalidIssuer);
|
||||
return Err(GoogleSignInError::JwtInvalidIssuer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +70,7 @@ pub fn decode_and_verify_token_claims_with_key(key: &RS256PublicKey, token: &str
|
||||
mod tests {
|
||||
use crate::certs::jwk_to_public_key::jwk_to_public_key;
|
||||
use crate::decode_and_verify_token_claims::decode_and_verify_token_claims;
|
||||
use crate::errors::VerifyError;
|
||||
use crate::error::google_sign_in_error::GoogleSignInError;
|
||||
use coarsetime::Duration;
|
||||
use jwt_simple::claims::Audiences;
|
||||
use jwt_simple::prelude::VerificationOptions;
|
||||
@@ -180,7 +178,7 @@ mod tests {
|
||||
};
|
||||
|
||||
match decode_and_verify_token_claims(&key_map, credential, Some(options)) {
|
||||
Err(VerifyError::JwtKeyMissing { requested_key}) => {
|
||||
Err(GoogleSignInError::JwtKeyMissing { requested_key}) => {
|
||||
assert_eq!(requested_key, "b2620d5e7f132b52afe8875cdf3776c064249d04");
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -201,7 +199,7 @@ mod tests {
|
||||
let credential = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImIyNjIwZDVlN2YxMzJiNTJhZmU4ODc1Y2RmMzc3NmMwNjQyNDlkMDQiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI3ODg4NDMwMzQyMzctdXFjZzh0YmdvZnJjZjF0bzM3ZTFicXBoZDkyNGphZjYuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI3ODg4NDMwMzQyMzctdXFjZzh0YmdvZnJjZjF0bzM3ZTFicXBoZDkyNGphZjYuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTMxMDE5Njc2MTIzOTY3OTM3NzciLCJlbWFpbCI6InZvY29kZXMyMDIwQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYmYiOjE3MjY3ODYxMDAsIm5hbWUiOiJWb2NvZGVzIFZvY29kZXMiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUNnOG9jTHoyLTJPYUFtME1ReFI2ajhDTnItUG84X1hyLWFyeUFUaUNuNGMwaV9UdURtTF9nPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IlZvY29kZXMiLCJmYW1pbHlfbmFtZSI6IlZvY29kZXMiLCJpYXQiOjE3MjY3ODY0MDAsImV4cCI6MTcyNjc5MDAwMCwianRpIjoiNGQ0NGVlYWMwNmNlNzlmYzBhYjIyNzBjZmVlYTMwZDhhY2Y3NzYxMyJ9.EYg71yIkvhxFGc8ZVCXeTOAmPAtLYDphHnkdf1sh8b_Jz4Y7S1DpmiTqQ1ytxu7J1xNixvdwhuIDzSlCvlxaFl8475GvAlyPTNtZtmWbFD5SRM_XHLOynijOp8WQ4nej-CHvT1KjjqMfkZ1EeQMoWk1H72PxPg_RiUgzsklkUs1wOkLAySk7R3EIAl7bIzpoY_WH2pxv9ccFpBtKDHaDqHkxAWBUQX0-G7ZXZBPVz07V28ZfdbzFDapjZaUFbumazh_-J2-9AA6JkcteF4h_gpbBcLYAuxt5bWI5FECWbYe42khwb93WJ5SK12Tt0EPoyzIObJs14NWGAajtHTg3wA";
|
||||
|
||||
match decode_and_verify_token_claims(&key_map, credential, None) {
|
||||
Err(VerifyError::JwtExpired) => {
|
||||
Err(GoogleSignInError::JwtExpired) => {
|
||||
// Expected error case
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -231,7 +229,7 @@ mod tests {
|
||||
};
|
||||
|
||||
match decode_and_verify_token_claims(&key_map, credential, Some(options)) {
|
||||
Err(VerifyError::JwtInvalidIssuer) => {
|
||||
Err(GoogleSignInError::JwtInvalidIssuer) => {
|
||||
// Expected error case
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -261,7 +259,7 @@ mod tests {
|
||||
};
|
||||
|
||||
match decode_and_verify_token_claims(&key_map, credential, Some(options)) {
|
||||
Err(VerifyError::JwtInvalidAudience) => {
|
||||
Err(GoogleSignInError::JwtInvalidAudience) => {
|
||||
// Expected error case
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// All failure modes surfaced by the `google_sign_in` crate.
|
||||
///
|
||||
/// Kept as a single flat enum on purpose — the crate is small enough that
|
||||
/// splitting into client/api/specific tiers (like beeble_client / gmicloud_client
|
||||
/// do) is overkill here.
|
||||
#[derive(Debug)]
|
||||
pub enum GoogleSignInError {
|
||||
// ── Cert download (Google's JWK endpoint) ──
|
||||
|
||||
/// HTTP error talking to Google's JWK cert endpoint.
|
||||
CertDownloadHttpError(reqwest::Error),
|
||||
|
||||
/// Google's cert endpoint returned a non-2xx status.
|
||||
CertDownloadBadStatus(reqwest::StatusCode),
|
||||
|
||||
// ── JWK parse ──
|
||||
|
||||
/// Failed to parse Google's JWK payload as JSON.
|
||||
JwkJsonParseFailed(serde_json::Error),
|
||||
|
||||
/// Failed to base64-decode a JWK key component (`n` or `e`).
|
||||
JwkBase64DecodeFailed(base64::DecodeError),
|
||||
|
||||
/// Failed to construct an RSA public key from JWK components.
|
||||
/// The wrapped string is `jwt_simple`'s formatted error.
|
||||
JwkRsaKeyConstructionFailed(String),
|
||||
|
||||
/// JWK payload contained no usable signing keys.
|
||||
JwkNoKeysFound,
|
||||
|
||||
// ── JWT header decode ──
|
||||
|
||||
/// JWT was malformed — couldn't split off the header segment.
|
||||
JwtMalformed,
|
||||
|
||||
/// Failed to base64-decode the JWT header segment.
|
||||
/// The wrapped string is `ct_codecs`'s formatted error.
|
||||
JwtHeaderBase64DecodeFailed(String),
|
||||
|
||||
/// Failed to parse the JWT header as JSON.
|
||||
JwtHeaderJsonParseFailed(serde_json::Error),
|
||||
|
||||
// ── JWT claim verification ──
|
||||
|
||||
/// JWT carried no `kid` and the keymap is empty — no key to verify with.
|
||||
JwtNoKeyId,
|
||||
|
||||
/// JWT requested a `kid` not present in the JWK keymap.
|
||||
/// Refreshing the JWK keymap may resolve.
|
||||
JwtKeyMissing { requested_key: String },
|
||||
|
||||
/// JWT has expired (was issued before the current clock + skew tolerance).
|
||||
JwtExpired,
|
||||
|
||||
/// JWT audience claim is absent or doesn't match the configured allowlist.
|
||||
JwtInvalidAudience,
|
||||
|
||||
/// JWT issuer claim is absent or doesn't match the configured allowlist.
|
||||
JwtInvalidIssuer,
|
||||
|
||||
/// JWT signature verification failed for some other reason.
|
||||
/// The wrapped string is `jwt_simple`'s formatted error.
|
||||
JwtVerifyFailed(String),
|
||||
|
||||
// ── Claims helpers ──
|
||||
|
||||
/// `audience_matches()` was called on claims that have no audience field.
|
||||
AudienceMissing,
|
||||
}
|
||||
|
||||
impl Error for GoogleSignInError {}
|
||||
|
||||
impl Display for GoogleSignInError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod google_sign_in_error;
|
||||
@@ -1,59 +0,0 @@
|
||||
use errors::AnyhowError;
|
||||
use std::error::Error;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VerifyError {
|
||||
/// Could not decode a JWT token.
|
||||
JwtDecodeError { source: AnyhowError },
|
||||
|
||||
/// The JWT has expired (was issued before the current
|
||||
/// clock + deadline + skew tolerance)
|
||||
JwtExpired,
|
||||
|
||||
/// The key requested by the JWT was not available.
|
||||
/// This likely means we need to refresh our JWK key set.
|
||||
JwtKeyMissing { requested_key: String },
|
||||
|
||||
/// The audience for the JWT claims was absent or unexpected.
|
||||
JwtInvalidAudience,
|
||||
|
||||
/// The issuer of the JWT claims was absent or unexpected.
|
||||
JwtInvalidIssuer,
|
||||
|
||||
/// Any other error type we haven't wrapped.
|
||||
AnyhowError(AnyhowError),
|
||||
}
|
||||
|
||||
impl Error for VerifyError {}
|
||||
|
||||
impl Display for VerifyError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::JwtDecodeError { source } => {
|
||||
write!(f, "JWT decode error: {}", source)
|
||||
}
|
||||
Self::JwtExpired => {
|
||||
write!(f, "JWT token has expired")
|
||||
}
|
||||
Self::JwtKeyMissing { requested_key } => {
|
||||
write!(f, "JWT requested key absent from JWK: {}", requested_key)
|
||||
}
|
||||
Self::JwtInvalidAudience => {
|
||||
write!(f, "Unexpected or missing JWT audience")
|
||||
}
|
||||
Self::JwtInvalidIssuer => {
|
||||
write!(f, "Unexpected or missing JWT issuer")
|
||||
}
|
||||
Self::AnyhowError(err) => {
|
||||
write!(f, "AnyhowError: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnyhowError> for VerifyError {
|
||||
fn from(value: AnyhowError) -> Self {
|
||||
Self::AnyhowError(value)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use errors::{anyhow, AnyhowResult};
|
||||
use crate::error::google_sign_in_error::GoogleSignInError;
|
||||
use jwt_simple::prelude::Base64UrlSafeNoPadding;
|
||||
use jwt_simple::reexports::ct_codecs::Decoder;
|
||||
use serde_derive::Deserialize;
|
||||
@@ -10,13 +10,15 @@ pub(crate) struct JwtHeader {
|
||||
// Other fields absent
|
||||
}
|
||||
|
||||
pub fn decode_jwt_header(jwt: &str) -> AnyhowResult<JwtHeader> {
|
||||
pub fn decode_jwt_header(jwt: &str) -> Result<JwtHeader, GoogleSignInError> {
|
||||
let mut parts = jwt.split('.');
|
||||
let jwt_header_b64 = parts.next().ok_or(anyhow!("could not split jwt"))?;
|
||||
let jwt_header_b64 = parts.next().ok_or(GoogleSignInError::JwtMalformed)?;
|
||||
|
||||
let jwt_header: JwtHeader = serde_json::from_slice(
|
||||
&Base64UrlSafeNoPadding::decode_to_vec(jwt_header_b64, None)?,
|
||||
)?;
|
||||
let decoded = Base64UrlSafeNoPadding::decode_to_vec(jwt_header_b64, None)
|
||||
.map_err(|err| GoogleSignInError::JwtHeaderBase64DecodeFailed(format!("{}", err)))?;
|
||||
|
||||
let jwt_header: JwtHeader = serde_json::from_slice(&decoded)
|
||||
.map_err(GoogleSignInError::JwtHeaderJsonParseFailed)?;
|
||||
|
||||
Ok(jwt_header)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
pub mod certs;
|
||||
pub mod claims;
|
||||
pub mod decode_and_verify_token_claims;
|
||||
pub mod errors;
|
||||
pub mod error;
|
||||
|
||||
pub(crate) mod jwt;
|
||||
|
||||
|
||||
+1
@@ -311,6 +311,7 @@ SELECT
|
||||
m.maybe_duration_millis,
|
||||
|
||||
m.maybe_prompt_token,
|
||||
prompts.maybe_other_args as maybe_other_prompt_args,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
|
||||
@@ -175,6 +175,7 @@ magic-crypt = "3.1.13" # encrypting sort IDs for the frontend so we don't leak i
|
||||
md-5 = "0.10.6" # gravatar
|
||||
|
||||
# Serialization
|
||||
erased-serde.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -45,15 +45,15 @@ pub async fn attempt_wallet_deduction_else_common_web_error(
|
||||
}
|
||||
WalletSpendError::SelectError(err) => {
|
||||
log::error!("SQL error (select) in attempt_wallet_deduction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
}
|
||||
WalletSpendError::SelectOptionalError(err) => {
|
||||
log::error!("SQL error (select optional) in attempt_wallet_deduction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
}
|
||||
WalletSpendError::SqlxError(err) => {
|
||||
log::error!("SQL error (sqlx) in attempt_wallet_deduction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
-532
@@ -1,532 +0,0 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::session::session_checker_error::SessionCheckerError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::RequireUserSessionError;
|
||||
use actix_artcraft::sessions::anonymous_visitor_tracking::avt_cookie_payload_error::AvtCookiePayloadError;
|
||||
use actix_artcraft::sessions::user_sessions::http_user_session_payload_error::HttpUserSessionPayloadError;
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError};
|
||||
use anyhow::anyhow;
|
||||
|
||||
/// An error type for actix-web handlers that wraps causal errors for debugging
|
||||
/// and paging while presenting safe, generic HTTP responses to users.
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ```ignore
|
||||
/// pub async fn my_handler(...) -> Result<Json<MyResponse>, AdvancedCommonWebError> {
|
||||
/// // sqlx errors, anyhow errors, session errors — all convert via ?
|
||||
/// let user = require_user_session_using_connection(&req, ...)?;
|
||||
/// let data = some_db_query(&pool).await?;
|
||||
///
|
||||
/// if data.is_none() {
|
||||
/// return Err(AdvancedCommonWebError::NotFound);
|
||||
/// }
|
||||
///
|
||||
/// Ok(Json(MyResponse { data }))
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Errors converted via `From<T>` become `UncaughtServerError` (always 500).
|
||||
/// The wrapped cause is never shown to users but is available to the error
|
||||
/// alerting middleware for paging and logging.
|
||||
#[derive(Clone)]
|
||||
pub enum AdvancedCommonWebError {
|
||||
/// 400 Bad Request with a user-facing message.
|
||||
BadInputWithSimpleMessage(String),
|
||||
|
||||
/// 401 Unauthorized.
|
||||
NotAuthorized,
|
||||
|
||||
/// 404 Not Found.
|
||||
NotFound,
|
||||
|
||||
/// 402 Payment Required.
|
||||
PaymentRequired,
|
||||
|
||||
/// 403 Forbidden.
|
||||
Forbidden,
|
||||
|
||||
/// 403 Forbidden — content was rejected by a content policy filter (e.g. NSFW).
|
||||
ContentPolicyRejected,
|
||||
|
||||
/// 403 Forbidden — content was rejected, with a user-facing message.
|
||||
ContentPolicyRejectedWithMessage(String),
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Stored in `Arc` so the error alerting middleware can clone it for paging
|
||||
/// without consuming the original.
|
||||
UncaughtServerError(Arc<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
/// Same as above, with a specified extra message string
|
||||
UncaughtServerErrorWithInternalMessage {
|
||||
internal_message: String,
|
||||
error: Arc<dyn std::error::Error + Send + Sync + 'static>
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Public accessors ===============
|
||||
|
||||
impl AdvancedCommonWebError {
|
||||
/// Wrap any error as an `UncaughtServerError`.
|
||||
pub fn from_error(error: impl std::error::Error + Send + Sync + 'static) -> Self {
|
||||
Self::UncaughtServerError(Arc::new(error))
|
||||
}
|
||||
|
||||
pub fn from_error_with_message(message: String, error: impl std::error::Error + Send + Sync + 'static) -> Self {
|
||||
Self::UncaughtServerErrorWithInternalMessage {
|
||||
internal_message: message,
|
||||
error: Arc::new(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap an `anyhow::Error` as an `UncaughtServerError`.
|
||||
/// (`anyhow::Error` doesn't implement `std::error::Error`, so `from_error` can't accept it.)
|
||||
pub fn from_anyhow_error(error: anyhow::Error) -> Self {
|
||||
let boxed: Box<dyn std::error::Error + Send + Sync> = error.into();
|
||||
Self::UncaughtServerError(Arc::from(boxed))
|
||||
}
|
||||
|
||||
pub fn server_error_with_message(msg: &str) -> Self {
|
||||
Self::from_anyhow_error(anyhow!("ServerErrorWithMessage: {:?}", msg))
|
||||
}
|
||||
|
||||
/// Extract the wrapped causal error (only present for `UncaughtServerError`).
|
||||
pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
|
||||
match self {
|
||||
Self::UncaughtServerError(err) => Some(err.as_ref()),
|
||||
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(error.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the wrapped causal error as an `Arc` (only present for `UncaughtServerError`).
|
||||
/// Useful for passing the error to the pager system without consuming the original.
|
||||
pub fn clone_cause_arc(&self) -> Option<Arc<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
match self {
|
||||
Self::UncaughtServerError(err) => Some(Arc::clone(err)),
|
||||
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(Arc::clone(error)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this is a server error (500).
|
||||
pub fn is_server_error(&self) -> bool {
|
||||
match self {
|
||||
Self::UncaughtServerError(_) => true,
|
||||
Self::UncaughtServerErrorWithInternalMessage { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Display / Debug / Error ===============
|
||||
|
||||
impl Display for AdvancedCommonWebError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::BadInputWithSimpleMessage(msg) => write!(f, "Bad input: {}", msg),
|
||||
Self::NotAuthorized => write!(f, "Not authorized"),
|
||||
Self::NotFound => write!(f, "Not found"),
|
||||
Self::PaymentRequired => write!(f, "Payment required"),
|
||||
Self::Forbidden => write!(f, "Forbidden"),
|
||||
Self::ContentPolicyRejected => write!(f, "Content policy rejected"),
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => write!(f, "Content policy rejected: {}", msg),
|
||||
Self::UncaughtServerError(err) => write!(f, "Server error: {}", err),
|
||||
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
|
||||
write!(f, "Server error: {}: {}", internal_message, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AdvancedCommonWebError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::BadInputWithSimpleMessage(msg) => write!(f, "BadInputWithSimpleMessage({:?})", msg),
|
||||
Self::NotAuthorized => write!(f, "NotAuthorized"),
|
||||
Self::NotFound => write!(f, "NotFound"),
|
||||
Self::PaymentRequired => write!(f, "PaymentRequired"),
|
||||
Self::Forbidden => write!(f, "Forbidden"),
|
||||
Self::ContentPolicyRejected => write!(f, "ContentPolicyRejected"),
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => write!(f, "ContentPolicyRejectedWithMessage({:?})", msg),
|
||||
Self::UncaughtServerError(err) => write!(f, "UncaughtServerError({:?})", err),
|
||||
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
|
||||
write!(f, "UncaughtServerErrorWithInternalMessage({:?}, {:?})", internal_message, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for AdvancedCommonWebError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::UncaughtServerError(err) => Some(err.as_ref()),
|
||||
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(error.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Actix ResponseError ===============
|
||||
|
||||
impl ResponseError for AdvancedCommonWebError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
Self::BadInputWithSimpleMessage(_) => StatusCode::BAD_REQUEST,
|
||||
Self::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
Self::NotFound => StatusCode::NOT_FOUND,
|
||||
Self::PaymentRequired => StatusCode::PAYMENT_REQUIRED,
|
||||
Self::Forbidden => StatusCode::FORBIDDEN,
|
||||
Self::ContentPolicyRejected => StatusCode::FORBIDDEN,
|
||||
Self::ContentPolicyRejectedWithMessage(_) => StatusCode::FORBIDDEN,
|
||||
Self::UncaughtServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::UncaughtServerErrorWithInternalMessage { .. } => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let status = self.status_code();
|
||||
|
||||
match self {
|
||||
Self::BadInputWithSimpleMessage(msg) => {
|
||||
HttpResponse::BadRequest()
|
||||
.json(JsonErrorWithMessage {
|
||||
success: false,
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
message: msg,
|
||||
})
|
||||
}
|
||||
Self::ContentPolicyRejected => {
|
||||
HttpResponseBuilder::new(status)
|
||||
.json(JsonErrorWithMessage {
|
||||
success: false,
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
message: "Content rejected by policy",
|
||||
})
|
||||
}
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => {
|
||||
HttpResponseBuilder::new(status)
|
||||
.json(JsonErrorWithMessage {
|
||||
success: false,
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
message: msg,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
HttpResponseBuilder::new(status)
|
||||
.json(JsonErrorWithoutMessage {
|
||||
success: false,
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== From impls (automatic ? conversion) ===============
|
||||
|
||||
impl From<sqlx::Error> for AdvancedCommonWebError {
|
||||
fn from(err: sqlx::Error) -> Self {
|
||||
Self::from_error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for AdvancedCommonWebError {
|
||||
fn from(err: anyhow::Error) -> Self {
|
||||
// anyhow::Error doesn't impl std::error::Error, so we go through Box -> Arc.
|
||||
let boxed: Box<dyn std::error::Error + Send + Sync> = err.into();
|
||||
Self::UncaughtServerError(Arc::from(boxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for AdvancedCommonWebError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
Self::from_error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequireUserSessionError> for AdvancedCommonWebError {
|
||||
fn from(value: RequireUserSessionError) -> Self {
|
||||
match value {
|
||||
RequireUserSessionError::NotAuthorized => Self::NotAuthorized,
|
||||
RequireUserSessionError::ServerError => Self::from_error(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HttpUserSessionPayloadError> for AdvancedCommonWebError {
|
||||
fn from(err: HttpUserSessionPayloadError) -> Self {
|
||||
if err.is_server_error() {
|
||||
Self::from_error(err)
|
||||
} else {
|
||||
Self::BadInputWithSimpleMessage("invalid session".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AvtCookiePayloadError> for AdvancedCommonWebError {
|
||||
fn from(err: AvtCookiePayloadError) -> Self {
|
||||
if err.is_server_error() {
|
||||
Self::from_error(err)
|
||||
} else {
|
||||
Self::BadInputWithSimpleMessage("invalid AVT cookie".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SessionCheckerError> for AdvancedCommonWebError {
|
||||
fn from(value: SessionCheckerError) -> Self {
|
||||
match value {
|
||||
// Bad / forged session cookie → 401 (don't page on this)
|
||||
// NOTE: If there's an elevated rate of across-the-board 401s,
|
||||
// then we probably misconfigured the HMAC secret
|
||||
SessionCheckerError::SessionPayload(_) => Self::NotAuthorized,
|
||||
// Underlying DB / cache errors → 500 with paging
|
||||
SessionCheckerError::Sqlx(err) => Self::from_error(err),
|
||||
// Likely Redis caching middleware
|
||||
SessionCheckerError::OtherError(err) => Self::from_anyhow_error(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommonWebError> for AdvancedCommonWebError {
|
||||
fn from(value: CommonWebError) -> Self {
|
||||
match value {
|
||||
CommonWebError::BadInputWithSimpleMessage(msg) => Self::BadInputWithSimpleMessage(msg),
|
||||
CommonWebError::NotAuthorized => Self::NotAuthorized,
|
||||
CommonWebError::NotFound => Self::NotFound,
|
||||
CommonWebError::PaymentRequired => Self::PaymentRequired,
|
||||
CommonWebError::ServerError => Self::from_error(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Serialization helpers (private) ===============
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonErrorWithoutMessage {
|
||||
success: bool,
|
||||
error_code: u16,
|
||||
error_code_str: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonErrorWithMessage<'a> {
|
||||
success: bool,
|
||||
error_code: u16,
|
||||
error_code_str: Option<&'static str>,
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
// =============== OpenAPI schema ===============
|
||||
|
||||
impl utoipa::PartialSchema for AdvancedCommonWebError {
|
||||
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.property(
|
||||
"success",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::Boolean),
|
||||
)
|
||||
.required("success")
|
||||
.property(
|
||||
"error_code",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::Integer)
|
||||
.format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
|
||||
utoipa::openapi::KnownFormat::Int32,
|
||||
))),
|
||||
)
|
||||
.required("error_code")
|
||||
.property(
|
||||
"error_code_str",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::String),
|
||||
)
|
||||
.property(
|
||||
"message",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::String)
|
||||
.description(Some("User-facing error message (present only for bad input errors)")),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl utoipa::ToSchema for AdvancedCommonWebError {
|
||||
fn name() -> std::borrow::Cow<'static, str> {
|
||||
std::borrow::Cow::Borrowed("AdvancedCommonWebError")
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Tests ===============
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_http::body::MessageBody;
|
||||
|
||||
#[test]
|
||||
fn bad_input_returns_400_with_message() {
|
||||
let error = AdvancedCommonWebError::BadInputWithSimpleMessage("name is required".to_string());
|
||||
assert_eq!(error.status_code(), StatusCode::BAD_REQUEST);
|
||||
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("\"message\":\"name is required\""));
|
||||
assert!(body.contains("\"error_code\":400"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_found_returns_404() {
|
||||
let error = AdvancedCommonWebError::NotFound;
|
||||
assert_eq!(error.status_code(), StatusCode::NOT_FOUND);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_authorized_returns_401() {
|
||||
let error = AdvancedCommonWebError::NotAuthorized;
|
||||
assert_eq!(error.status_code(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payment_required_returns_402() {
|
||||
let error = AdvancedCommonWebError::PaymentRequired;
|
||||
assert_eq!(error.status_code(), StatusCode::PAYMENT_REQUIRED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uncaught_io_error_returns_500_and_hides_cause() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk exploded");
|
||||
let error = AdvancedCommonWebError::from_error(io_err);
|
||||
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
|
||||
// Cause is accessible for middleware
|
||||
let cause = error.cause().unwrap();
|
||||
assert_eq!(format!("{}", cause), "disk exploded");
|
||||
|
||||
// But never shown to the user in the HTTP response
|
||||
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("disk exploded"));
|
||||
assert!(body.contains("\"error_code\":500"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anyhow_error_converts_to_500_via_from() {
|
||||
let error: AdvancedCommonWebError = anyhow::anyhow!("something broke in the pipeline").into();
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
|
||||
let cause = error.cause().unwrap();
|
||||
assert!(format!("{}", cause).contains("something broke in the pipeline"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_anyhow_error_preserves_context_chain() {
|
||||
let inner = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "connection refused");
|
||||
let with_context = anyhow::Error::new(inner).context("failed to connect to database");
|
||||
let error: AdvancedCommonWebError = with_context.into();
|
||||
|
||||
assert!(error.is_server_error());
|
||||
let cause = error.cause().unwrap();
|
||||
let display = format!("{}", cause);
|
||||
assert!(display.contains("failed to connect to database"));
|
||||
|
||||
// The original io error should be in the source chain
|
||||
let source = cause.source();
|
||||
assert!(source.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_user_session_not_authorized_maps_to_401() {
|
||||
let error: AdvancedCommonWebError = RequireUserSessionError::NotAuthorized.into();
|
||||
assert_eq!(error.status_code(), StatusCode::UNAUTHORIZED);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_user_session_server_error_wraps_cause() {
|
||||
let error: AdvancedCommonWebError = RequireUserSessionError::ServerError.into();
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
assert!(error.cause().is_some());
|
||||
assert!(format!("{}", error.cause().unwrap()).contains("ServerError"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_json_error_converts_to_500() {
|
||||
let bad_json = "not json at all{{{";
|
||||
let serde_err: serde_json::Error = serde_json::from_str::<serde_json::Value>(bad_json).unwrap_err();
|
||||
let error: AdvancedCommonWebError = serde_err.into();
|
||||
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
let cause = error.cause().unwrap();
|
||||
assert!(format!("{}", cause).contains("expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_source_returns_wrapped_error() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "root cause");
|
||||
let error = AdvancedCommonWebError::from_error(io_err);
|
||||
|
||||
let source = std::error::Error::source(&error);
|
||||
assert!(source.is_some());
|
||||
assert_eq!(format!("{}", source.unwrap()), "root cause");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_server_errors_have_no_source() {
|
||||
assert!(std::error::Error::source(&AdvancedCommonWebError::NotFound).is_none());
|
||||
assert!(std::error::Error::source(&AdvancedCommonWebError::NotAuthorized).is_none());
|
||||
assert!(std::error::Error::source(&AdvancedCommonWebError::PaymentRequired).is_none());
|
||||
assert!(std::error::Error::source(&AdvancedCommonWebError::Forbidden).is_none());
|
||||
assert!(std::error::Error::source(&AdvancedCommonWebError::ContentPolicyRejected).is_none());
|
||||
assert!(std::error::Error::source(&AdvancedCommonWebError::ContentPolicyRejectedWithMessage("test".to_string())).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forbidden_returns_403() {
|
||||
let error = AdvancedCommonWebError::Forbidden;
|
||||
assert_eq!(error.status_code(), StatusCode::FORBIDDEN);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_policy_rejected_returns_403() {
|
||||
let error = AdvancedCommonWebError::ContentPolicyRejected;
|
||||
assert_eq!(error.status_code(), StatusCode::FORBIDDEN);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_policy_rejected_with_message_returns_403() {
|
||||
let error = AdvancedCommonWebError::ContentPolicyRejectedWithMessage("NSFW detected".to_string());
|
||||
assert_eq!(error.status_code(), StatusCode::FORBIDDEN);
|
||||
assert!(!error.is_server_error());
|
||||
|
||||
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("\"message\":\"NSFW detected\""));
|
||||
assert!(body.contains("\"error_code\":403"));
|
||||
}
|
||||
}
|
||||
+525
-70
@@ -1,103 +1,313 @@
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError};
|
||||
use log::{error, warn};
|
||||
use mysql_queries::errors::mysql_error::{MysqlCrateErrorSubtype, MysqlError};
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::session::session_checker_error::SessionCheckerError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session::RequireUserSessionError;
|
||||
use actix_artcraft::sessions::anonymous_visitor_tracking::avt_cookie_payload_error::AvtCookiePayloadError;
|
||||
use actix_artcraft::sessions::user_sessions::http_user_session_payload_error::HttpUserSessionPayloadError;
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{HttpResponse, HttpResponseBuilder, ResponseError};
|
||||
use anyhow::anyhow;
|
||||
use mysql_queries::errors::mysql_error::{MysqlCrateErrorSubtype, MysqlError};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, utoipa::ToSchema)]
|
||||
/// An error type for actix-web handlers that wraps causal errors for debugging
|
||||
/// and paging while presenting safe, generic HTTP responses to users.
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ```ignore
|
||||
/// pub async fn my_handler(...) -> Result<Json<MyResponse>, CommonWebError> {
|
||||
/// // sqlx errors, anyhow errors, session errors — all convert via ?
|
||||
/// let user = require_user_session_using_connection(&req, ...)?;
|
||||
/// let data = some_db_query(&pool).await?;
|
||||
///
|
||||
/// if data.is_none() {
|
||||
/// return Err(CommonWebError::NotFound);
|
||||
/// }
|
||||
///
|
||||
/// Ok(Json(MyResponse { data }))
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Errors converted via `From<T>` become `UncaughtServerError` (always 500).
|
||||
/// The wrapped cause is never shown to users but is available to the error
|
||||
/// alerting middleware for paging and logging.
|
||||
#[derive(Clone)]
|
||||
pub enum CommonWebError {
|
||||
// TODO: Bad input with error fields vector.
|
||||
/// Bad input with a user-facing error message.
|
||||
/// 400 Bad Request with a user-facing message.
|
||||
BadInputWithSimpleMessage(String),
|
||||
/// Entity not found.
|
||||
NotFound,
|
||||
/// Not authorized to perform the action.
|
||||
|
||||
/// 400 Bad Request with an endpoint-specific JSON body.
|
||||
/// The caller hands over any `Serialize` struct; the middleware writes it
|
||||
/// directly as the response body with `Content-Type: application/json`.
|
||||
/// Construct via [`CommonWebError::bad_input_tailored_response`].
|
||||
///
|
||||
/// Stored as `Arc<dyn erased_serde::Serialize + Send + Sync + 'static>`:
|
||||
/// `serde::Serialize` isn't object-safe (its method is generic over
|
||||
/// `Serializer`), so we use `erased_serde::Serialize` — a sibling trait
|
||||
/// with a non-generic method, auto-implemented for every `T: Serialize`.
|
||||
/// `Arc` (instead of `Box`) keeps the enum `Clone`.
|
||||
BadInputTailoredResponse(Arc<dyn erased_serde::Serialize + Send + Sync + 'static>),
|
||||
|
||||
/// 401 Unauthorized.
|
||||
NotAuthorized,
|
||||
/// Server error with no reason.
|
||||
ServerError,
|
||||
/// Payment required with no extra info
|
||||
|
||||
/// 404 Not Found.
|
||||
NotFound,
|
||||
|
||||
/// 402 Payment Required.
|
||||
PaymentRequired,
|
||||
}
|
||||
|
||||
impl Error for CommonWebError {}
|
||||
/// 403 Forbidden.
|
||||
Forbidden,
|
||||
|
||||
impl Display for CommonWebError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
/// 403 Forbidden — content was rejected by a content policy filter (e.g. NSFW).
|
||||
ContentPolicyRejected,
|
||||
|
||||
/// 403 Forbidden — content was rejected, with a user-facing message.
|
||||
ContentPolicyRejectedWithMessage(String),
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Stored in `Arc` so the error alerting middleware can clone it for paging
|
||||
/// without consuming the original.
|
||||
UncaughtServerError(Arc<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
/// Same as above, with a specified extra message string
|
||||
UncaughtServerErrorWithInternalMessage {
|
||||
internal_message: String,
|
||||
error: Arc<dyn std::error::Error + Send + Sync + 'static>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct SerializeSimpleErrorWithoutMessage<'a> {
|
||||
success: bool,
|
||||
error_code: u16,
|
||||
error_code_str: Option<&'a str>,
|
||||
// =============== Public accessors ===============
|
||||
|
||||
impl CommonWebError {
|
||||
/// Wrap any error as an `UncaughtServerError`.
|
||||
pub fn from_error(error: impl std::error::Error + Send + Sync + 'static) -> Self {
|
||||
Self::UncaughtServerError(Arc::new(error))
|
||||
}
|
||||
|
||||
pub fn from_error_with_message(message: String, error: impl std::error::Error + Send + Sync + 'static) -> Self {
|
||||
Self::UncaughtServerErrorWithInternalMessage {
|
||||
internal_message: message,
|
||||
error: Arc::new(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap an `anyhow::Error` as an `UncaughtServerError`.
|
||||
/// (`anyhow::Error` doesn't implement `std::error::Error`, so `from_error` can't accept it.)
|
||||
pub fn from_anyhow_error(error: anyhow::Error) -> Self {
|
||||
let boxed: Box<dyn std::error::Error + Send + Sync> = error.into();
|
||||
Self::UncaughtServerError(Arc::from(boxed))
|
||||
}
|
||||
|
||||
pub fn server_error_with_message(msg: &str) -> Self {
|
||||
Self::from_anyhow_error(anyhow!("ServerErrorWithMessage: {:?}", msg))
|
||||
}
|
||||
|
||||
/// Build a 400 Bad Request whose body is the JSON-serialized form of `body`.
|
||||
///
|
||||
/// Use this when an endpoint needs to return a structured validation payload
|
||||
/// (per-field errors, etc.) rather than the generic `{success, error_code, message}`
|
||||
/// envelope used by [`BadInputWithSimpleMessage`]. The caller passes its own
|
||||
/// `Serialize` type directly — no envelope, no `serde_json::Value` boxing
|
||||
/// at the callsite.
|
||||
/// Build a 400 Bad Request whose body is the JSON-serialized form of `body`.
|
||||
///
|
||||
/// The caller passes its typed struct directly. Serialization is deferred to
|
||||
/// response time, when the middleware writes the JSON straight onto the wire.
|
||||
pub fn bad_input_tailored_response<T>(body: T) -> Self
|
||||
where
|
||||
T: Serialize + Send + Sync + 'static,
|
||||
{
|
||||
Self::BadInputTailoredResponse(Arc::new(body))
|
||||
}
|
||||
|
||||
/// Extract the wrapped causal error (only present for `UncaughtServerError`).
|
||||
pub fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
|
||||
match self {
|
||||
Self::UncaughtServerError(err) => Some(err.as_ref()),
|
||||
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(error.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the wrapped causal error as an `Arc` (only present for `UncaughtServerError`).
|
||||
/// Useful for passing the error to the pager system without consuming the original.
|
||||
pub fn clone_cause_arc(&self) -> Option<Arc<dyn std::error::Error + Send + Sync + 'static>> {
|
||||
match self {
|
||||
Self::UncaughtServerError(err) => Some(Arc::clone(err)),
|
||||
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(Arc::clone(error)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this is a server error (500).
|
||||
pub fn is_server_error(&self) -> bool {
|
||||
match self {
|
||||
Self::UncaughtServerError(_) => true,
|
||||
Self::UncaughtServerErrorWithInternalMessage { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Display / Debug / Error ===============
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct SerializeSimpleErrorWithMessage<'a> {
|
||||
success: bool,
|
||||
error_code: u16,
|
||||
error_code_str: Option<&'a str>,
|
||||
message: &'a str,
|
||||
impl Display for CommonWebError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::BadInputWithSimpleMessage(msg) => write!(f, "Bad input: {}", msg),
|
||||
Self::BadInputTailoredResponse(payload) => {
|
||||
match serde_json::to_string(payload) {
|
||||
Ok(s) => write!(f, "Bad input (tailored): {}", s),
|
||||
Err(_) => write!(f, "Bad input (tailored): <unserializable payload>"),
|
||||
}
|
||||
}
|
||||
Self::NotAuthorized => write!(f, "Not authorized"),
|
||||
Self::NotFound => write!(f, "Not found"),
|
||||
Self::PaymentRequired => write!(f, "Payment required"),
|
||||
Self::Forbidden => write!(f, "Forbidden"),
|
||||
Self::ContentPolicyRejected => write!(f, "Content policy rejected"),
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => write!(f, "Content policy rejected: {}", msg),
|
||||
Self::UncaughtServerError(err) => write!(f, "Server error: {}", err),
|
||||
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
|
||||
write!(f, "Server error: {}: {}", internal_message, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for CommonWebError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::BadInputWithSimpleMessage(msg) => write!(f, "BadInputWithSimpleMessage({:?})", msg),
|
||||
Self::BadInputTailoredResponse(payload) => {
|
||||
match serde_json::to_string(payload) {
|
||||
Ok(s) => write!(f, "BadInputTailoredResponse({})", s),
|
||||
Err(_) => write!(f, "BadInputTailoredResponse(<unserializable>)"),
|
||||
}
|
||||
}
|
||||
Self::NotAuthorized => write!(f, "NotAuthorized"),
|
||||
Self::NotFound => write!(f, "NotFound"),
|
||||
Self::PaymentRequired => write!(f, "PaymentRequired"),
|
||||
Self::Forbidden => write!(f, "Forbidden"),
|
||||
Self::ContentPolicyRejected => write!(f, "ContentPolicyRejected"),
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => write!(f, "ContentPolicyRejectedWithMessage({:?})", msg),
|
||||
Self::UncaughtServerError(err) => write!(f, "UncaughtServerError({:?})", err),
|
||||
Self::UncaughtServerErrorWithInternalMessage { internal_message, error } => {
|
||||
write!(f, "UncaughtServerErrorWithInternalMessage({:?}, {:?})", internal_message, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CommonWebError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::UncaughtServerError(err) => Some(err.as_ref()),
|
||||
Self::UncaughtServerErrorWithInternalMessage { error, .. } => Some(error.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Actix ResponseError ===============
|
||||
|
||||
impl ResponseError for CommonWebError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
CommonWebError::BadInputWithSimpleMessage(_) => StatusCode::BAD_REQUEST,
|
||||
CommonWebError::NotFound => StatusCode::NOT_FOUND,
|
||||
CommonWebError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
CommonWebError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CommonWebError::PaymentRequired => StatusCode::PAYMENT_REQUIRED,
|
||||
Self::BadInputWithSimpleMessage(_) => StatusCode::BAD_REQUEST,
|
||||
Self::BadInputTailoredResponse(_) => StatusCode::BAD_REQUEST,
|
||||
Self::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
Self::NotFound => StatusCode::NOT_FOUND,
|
||||
Self::PaymentRequired => StatusCode::PAYMENT_REQUIRED,
|
||||
Self::Forbidden => StatusCode::FORBIDDEN,
|
||||
Self::ContentPolicyRejected => StatusCode::FORBIDDEN,
|
||||
Self::ContentPolicyRejectedWithMessage(_) => StatusCode::FORBIDDEN,
|
||||
Self::UncaughtServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::UncaughtServerErrorWithInternalMessage { .. } => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let status = self.status_code();
|
||||
|
||||
match self {
|
||||
CommonWebError::BadInputWithSimpleMessage(msg) => {
|
||||
Self::BadInputWithSimpleMessage(msg) => {
|
||||
HttpResponse::BadRequest()
|
||||
.json(SerializeSimpleErrorWithMessage {
|
||||
.json(JsonErrorWithMessage {
|
||||
success: false,
|
||||
error_code: self.status_code().as_u16(),
|
||||
error_code_str: self.status_code().canonical_reason(),
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
message: msg,
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
HttpResponseBuilder::new(self.status_code())
|
||||
.json(SerializeSimpleErrorWithoutMessage {
|
||||
}
|
||||
Self::BadInputTailoredResponse(payload) => {
|
||||
HttpResponse::BadRequest().json(payload)
|
||||
}
|
||||
Self::ContentPolicyRejected => {
|
||||
HttpResponseBuilder::new(status)
|
||||
.json(JsonErrorWithMessage {
|
||||
success: false,
|
||||
error_code: self.status_code().as_u16(),
|
||||
error_code_str: self.status_code().canonical_reason(),
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
message: "Content rejected by policy",
|
||||
})
|
||||
}
|
||||
Self::ContentPolicyRejectedWithMessage(msg) => {
|
||||
HttpResponseBuilder::new(status)
|
||||
.json(JsonErrorWithMessage {
|
||||
success: false,
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
message: msg,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
HttpResponseBuilder::new(status)
|
||||
.json(JsonErrorWithoutMessage {
|
||||
success: false,
|
||||
error_code: status.as_u16(),
|
||||
error_code_str: status.canonical_reason(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== From impls (automatic ? conversion) ===============
|
||||
|
||||
impl From<sqlx::Error> for CommonWebError {
|
||||
fn from(value: sqlx::Error) -> Self {
|
||||
error!("SQLx error: {:?}", value);
|
||||
CommonWebError::ServerError
|
||||
fn from(err: sqlx::Error) -> Self {
|
||||
Self::from_error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<MysqlError<T>> for CommonWebError
|
||||
where
|
||||
T: MysqlCrateErrorSubtype + Send + Sync + 'static,
|
||||
{
|
||||
fn from(err: MysqlError<T>) -> Self {
|
||||
Self::from_error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for CommonWebError {
|
||||
fn from(value: anyhow::Error) -> Self {
|
||||
error!("Uncaught anyhow::Error being coerced to CommonWebError : {:?}", value);
|
||||
CommonWebError::ServerError
|
||||
fn from(err: anyhow::Error) -> Self {
|
||||
// anyhow::Error doesn't impl std::error::Error, so we go through Box -> Arc.
|
||||
let boxed: Box<dyn std::error::Error + Send + Sync> = err.into();
|
||||
Self::UncaughtServerError(Arc::from(boxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: MysqlCrateErrorSubtype> From<MysqlError<T>> for CommonWebError {
|
||||
fn from(value: MysqlError<T>) -> Self {
|
||||
error!("MysqlError being coerced to CommonWebError : {:?}", value);
|
||||
CommonWebError::ServerError
|
||||
impl From<serde_json::Error> for CommonWebError {
|
||||
fn from(err: serde_json::Error) -> Self {
|
||||
Self::from_error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +315,27 @@ impl From<RequireUserSessionError> for CommonWebError {
|
||||
fn from(value: RequireUserSessionError) -> Self {
|
||||
match value {
|
||||
RequireUserSessionError::NotAuthorized => Self::NotAuthorized,
|
||||
RequireUserSessionError::ServerError => Self::ServerError,
|
||||
RequireUserSessionError::ServerError => Self::from_error(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HttpUserSessionPayloadError> for CommonWebError {
|
||||
fn from(err: HttpUserSessionPayloadError) -> Self {
|
||||
if err.is_server_error() {
|
||||
Self::from_error(err)
|
||||
} else {
|
||||
Self::BadInputWithSimpleMessage("invalid session".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AvtCookiePayloadError> for CommonWebError {
|
||||
fn from(err: AvtCookiePayloadError) -> Self {
|
||||
if err.is_server_error() {
|
||||
Self::from_error(err)
|
||||
} else {
|
||||
Self::BadInputWithSimpleMessage("invalid AVT cookie".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,35 +343,260 @@ impl From<RequireUserSessionError> for CommonWebError {
|
||||
impl From<SessionCheckerError> for CommonWebError {
|
||||
fn from(value: SessionCheckerError) -> Self {
|
||||
match value {
|
||||
// Bad / forged session cookie → 401
|
||||
// Bad / forged session cookie → 401 (don't page on this)
|
||||
// NOTE: If there's an elevated rate of across-the-board 401s,
|
||||
// then we probably misconfigured the HMAC secret
|
||||
SessionCheckerError::SessionPayload(_) => Self::NotAuthorized,
|
||||
// Underlying DB / cache errors → 500
|
||||
SessionCheckerError::Sqlx(err) => {
|
||||
error!("SessionChecker sqlx error: {:?}", err);
|
||||
Self::ServerError
|
||||
},
|
||||
// Underlying DB / cache errors → 500 with paging
|
||||
SessionCheckerError::Sqlx(err) => Self::from_error(err),
|
||||
// Likely Redis caching middleware
|
||||
SessionCheckerError::OtherError(err) => {
|
||||
error!("SessionChecker other error: {:?}", err);
|
||||
Self::ServerError
|
||||
},
|
||||
SessionCheckerError::OtherError(err) => Self::from_anyhow_error(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Serialization helpers (private) ===============
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonErrorWithoutMessage {
|
||||
success: bool,
|
||||
error_code: u16,
|
||||
error_code_str: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonErrorWithMessage<'a> {
|
||||
success: bool,
|
||||
error_code: u16,
|
||||
error_code_str: Option<&'static str>,
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
// =============== OpenAPI schema ===============
|
||||
|
||||
impl utoipa::PartialSchema for CommonWebError {
|
||||
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.property(
|
||||
"success",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::Boolean),
|
||||
)
|
||||
.required("success")
|
||||
.property(
|
||||
"error_code",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::Integer)
|
||||
.format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
|
||||
utoipa::openapi::KnownFormat::Int32,
|
||||
))),
|
||||
)
|
||||
.required("error_code")
|
||||
.property(
|
||||
"error_code_str",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::String),
|
||||
)
|
||||
.property(
|
||||
"message",
|
||||
utoipa::openapi::ObjectBuilder::new()
|
||||
.schema_type(utoipa::openapi::schema::Type::String)
|
||||
.description(Some("User-facing error message (present only for bad input errors)")),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl utoipa::ToSchema for CommonWebError {
|
||||
fn name() -> std::borrow::Cow<'static, str> {
|
||||
std::borrow::Cow::Borrowed("CommonWebError")
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Tests ===============
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_http::body::MessageBody;
|
||||
|
||||
#[test]
|
||||
fn test_bad_input_json() {
|
||||
let error = CommonWebError::BadInputWithSimpleMessage("foo bar baz".to_string());
|
||||
fn bad_input_returns_400_with_message() {
|
||||
let error = CommonWebError::BadInputWithSimpleMessage("name is required".to_string());
|
||||
assert_eq!(error.status_code(), StatusCode::BAD_REQUEST);
|
||||
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_eq!(body, "{\"success\":false,\"error_code\":400,\"error_code_str\":\"Bad Request\",\"message\":\"foo bar baz\"}");
|
||||
assert!(body.contains("\"message\":\"name is required\""));
|
||||
assert!(body.contains("\"error_code\":400"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_input_tailored_response_serializes_payload_verbatim() {
|
||||
#[derive(Serialize)]
|
||||
struct Sample {
|
||||
reason: &'static str,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
let error = CommonWebError::bad_input_tailored_response(Sample {
|
||||
reason: "no good",
|
||||
count: 3,
|
||||
});
|
||||
assert_eq!(error.status_code(), StatusCode::BAD_REQUEST);
|
||||
assert!(!error.is_server_error());
|
||||
assert!(error.cause().is_none());
|
||||
|
||||
let response = error.error_response();
|
||||
let content_type = response.headers().get("content-type").unwrap().to_str().unwrap();
|
||||
assert!(content_type.starts_with("application/json"));
|
||||
|
||||
let bytes = response.into_body().try_into_bytes().unwrap();
|
||||
let body = String::from_utf8(bytes.to_vec()).unwrap();
|
||||
// Body is the raw serialized payload — no envelope.
|
||||
assert_eq!(body, r#"{"reason":"no good","count":3}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_found_returns_404() {
|
||||
let error = CommonWebError::NotFound;
|
||||
assert_eq!(error.status_code(), StatusCode::NOT_FOUND);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_authorized_returns_401() {
|
||||
let error = CommonWebError::NotAuthorized;
|
||||
assert_eq!(error.status_code(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payment_required_returns_402() {
|
||||
let error = CommonWebError::PaymentRequired;
|
||||
assert_eq!(error.status_code(), StatusCode::PAYMENT_REQUIRED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uncaught_io_error_returns_500_and_hides_cause() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk exploded");
|
||||
let error = CommonWebError::from_error(io_err);
|
||||
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
|
||||
// Cause is accessible for middleware
|
||||
let cause = error.cause().unwrap();
|
||||
assert_eq!(format!("{}", cause), "disk exploded");
|
||||
|
||||
// But never shown to the user in the HTTP response
|
||||
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("disk exploded"));
|
||||
assert!(body.contains("\"error_code\":500"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anyhow_error_converts_to_500_via_from() {
|
||||
let error: CommonWebError = anyhow::anyhow!("something broke in the pipeline").into();
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
|
||||
let cause = error.cause().unwrap();
|
||||
assert!(format!("{}", cause).contains("something broke in the pipeline"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_anyhow_error_preserves_context_chain() {
|
||||
let inner = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "connection refused");
|
||||
let with_context = anyhow::Error::new(inner).context("failed to connect to database");
|
||||
let error: CommonWebError = with_context.into();
|
||||
|
||||
assert!(error.is_server_error());
|
||||
let cause = error.cause().unwrap();
|
||||
let display = format!("{}", cause);
|
||||
assert!(display.contains("failed to connect to database"));
|
||||
|
||||
// The original io error should be in the source chain
|
||||
let source = cause.source();
|
||||
assert!(source.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_user_session_not_authorized_maps_to_401() {
|
||||
let error: CommonWebError = RequireUserSessionError::NotAuthorized.into();
|
||||
assert_eq!(error.status_code(), StatusCode::UNAUTHORIZED);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn require_user_session_server_error_wraps_cause() {
|
||||
let error: CommonWebError = RequireUserSessionError::ServerError.into();
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
assert!(error.cause().is_some());
|
||||
assert!(format!("{}", error.cause().unwrap()).contains("ServerError"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_json_error_converts_to_500() {
|
||||
let bad_json = "not json at all{{{";
|
||||
let serde_err: serde_json::Error = serde_json::from_str::<serde_json::Value>(bad_json).unwrap_err();
|
||||
let error: CommonWebError = serde_err.into();
|
||||
|
||||
assert_eq!(error.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
assert!(error.is_server_error());
|
||||
let cause = error.cause().unwrap();
|
||||
assert!(format!("{}", cause).contains("expected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_source_returns_wrapped_error() {
|
||||
let io_err = std::io::Error::new(std::io::ErrorKind::Other, "root cause");
|
||||
let error = CommonWebError::from_error(io_err);
|
||||
|
||||
let source = std::error::Error::source(&error);
|
||||
assert!(source.is_some());
|
||||
assert_eq!(format!("{}", source.unwrap()), "root cause");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_server_errors_have_no_source() {
|
||||
assert!(std::error::Error::source(&CommonWebError::NotFound).is_none());
|
||||
assert!(std::error::Error::source(&CommonWebError::NotAuthorized).is_none());
|
||||
assert!(std::error::Error::source(&CommonWebError::PaymentRequired).is_none());
|
||||
assert!(std::error::Error::source(&CommonWebError::Forbidden).is_none());
|
||||
assert!(std::error::Error::source(&CommonWebError::ContentPolicyRejected).is_none());
|
||||
assert!(std::error::Error::source(&CommonWebError::ContentPolicyRejectedWithMessage("test".to_string())).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forbidden_returns_403() {
|
||||
let error = CommonWebError::Forbidden;
|
||||
assert_eq!(error.status_code(), StatusCode::FORBIDDEN);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_policy_rejected_returns_403() {
|
||||
let error = CommonWebError::ContentPolicyRejected;
|
||||
assert_eq!(error.status_code(), StatusCode::FORBIDDEN);
|
||||
assert!(!error.is_server_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn content_policy_rejected_with_message_returns_403() {
|
||||
let error = CommonWebError::ContentPolicyRejectedWithMessage("NSFW detected".to_string());
|
||||
assert_eq!(error.status_code(), StatusCode::FORBIDDEN);
|
||||
assert!(!error.is_server_error());
|
||||
|
||||
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("\"message\":\"NSFW detected\""));
|
||||
assert!(body.contains("\"error_code\":403"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod advanced_common_web_error;
|
||||
pub mod common_web_error;
|
||||
pub mod media;
|
||||
pub mod media_file_origin_details;
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ pub async fn refund_wallet_after_api_failure(
|
||||
"Failed to begin refund transaction after API failure (ledger {}): {:?}",
|
||||
ledger_entry_token.as_str(), err
|
||||
);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
match try_to_refund_ledger_entry(ledger_entry_token, &mut transaction).await {
|
||||
@@ -29,7 +29,7 @@ pub async fn refund_wallet_after_api_failure(
|
||||
"Failed to commit refund after API failure (ledger {}): {:?}",
|
||||
ledger_entry_token.as_str(), err
|
||||
);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
Ok(WalletRefundOutcome::AlreadyRefunded) => {
|
||||
@@ -45,7 +45,7 @@ pub async fn refund_wallet_after_api_failure(
|
||||
ledger_entry_token.as_str(), err
|
||||
);
|
||||
let _ = transaction.rollback().await;
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@ use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use log::warn;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
|
||||
// =============== Response ===============
|
||||
|
||||
@@ -38,17 +38,17 @@ pub struct SubscriptionProductKey {
|
||||
pub async fn list_active_user_subscriptions_handler(
|
||||
http_request: HttpRequest,
|
||||
internal_user_lookup: web::Data<dyn InternalUserLookup>,
|
||||
) -> Result<Json<ListActiveUserSubscriptionsResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListActiveUserSubscriptionsResponse>, CommonWebError> {
|
||||
let maybe_user_metadata = internal_user_lookup
|
||||
.lookup_user_from_http_request(&http_request)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error looking up user: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let user_metadata = match maybe_user_metadata {
|
||||
None => return Err(AdvancedCommonWebError::NotAuthorized),
|
||||
None => return Err(CommonWebError::NotAuthorized),
|
||||
Some(user_metadata) => user_metadata,
|
||||
};
|
||||
|
||||
|
||||
+16
-16
@@ -23,7 +23,7 @@ use seedance2pro_client::requests::upload_file::upload_file::{upload_file, Uploa
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use url_utils::extension::extract_extension_from_url::{extract_extension_from_url, ExtractExtensions};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
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;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
@@ -50,7 +50,7 @@ pub async fn create_character_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<CreateCharacterRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<CreateCharacterResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<CreateCharacterResponse>, CommonWebError> {
|
||||
|
||||
// --- Auth ---
|
||||
|
||||
@@ -64,12 +64,12 @@ pub async fn create_character_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
AdvancedCommonWebError::from(e)
|
||||
CommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => return Err(AdvancedCommonWebError::NotAuthorized),
|
||||
None => return Err(CommonWebError::NotAuthorized),
|
||||
};
|
||||
|
||||
let user_token = &user_session.user_token;
|
||||
@@ -78,7 +78,7 @@ pub async fn create_character_handler(
|
||||
// --- Validate input ---
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
// For now we use the model name as the character name and description.
|
||||
@@ -89,7 +89,7 @@ pub async fn create_character_handler(
|
||||
|
||||
if let Some(desc) = character_description {
|
||||
if desc.len() > CHARACTER_MAX_DESCRIPTION_LENGTH {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
format!("Description exceeds maximum length of {} characters.", CHARACTER_MAX_DESCRIPTION_LENGTH),
|
||||
));
|
||||
}
|
||||
@@ -108,7 +108,7 @@ pub async fn create_character_handler(
|
||||
.get(&request.image_media_token)
|
||||
.ok_or_else(|| {
|
||||
warn!("Image media token not found: {:?}", request.image_media_token);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("Image media file not found.".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("Image media file not found.".to_string())
|
||||
})?;
|
||||
|
||||
// --- Insert idempotency token ---
|
||||
@@ -117,7 +117,7 @@ pub async fn create_character_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// --- Upload image to Kinovi CDN ---
|
||||
@@ -148,7 +148,7 @@ pub async fn create_character_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error calling Kinovi generate_character: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!(
|
||||
@@ -167,7 +167,7 @@ pub async fn create_character_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let character_token = create_pending_character(CreatePendingCharacterArgs {
|
||||
@@ -204,7 +204,7 @@ pub async fn create_character_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting inference job: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Created inference job: {}", apriori_job_token);
|
||||
@@ -214,7 +214,7 @@ pub async fn create_character_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(CreateCharacterResponse {
|
||||
@@ -229,7 +229,7 @@ pub async fn create_character_handler(
|
||||
async fn upload_to_kinovi(
|
||||
cookies: &str,
|
||||
our_cdn_url: &url::Url,
|
||||
) -> Result<String, AdvancedCommonWebError> {
|
||||
) -> Result<String, CommonWebError> {
|
||||
let extension = extract_extension_from_url(our_cdn_url, &ExtractExtensions::All)
|
||||
.map(|ext| ext.without_period().to_string())
|
||||
.unwrap_or_else(|| "png".to_string());
|
||||
@@ -240,7 +240,7 @@ async fn upload_to_kinovi(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error downloading media file from CDN: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?
|
||||
.to_vec();
|
||||
|
||||
@@ -252,7 +252,7 @@ async fn upload_to_kinovi(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error preparing Kinovi file upload: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let upload_result = upload_file(UploadFileArgs {
|
||||
@@ -263,7 +263,7 @@ async fn upload_to_kinovi(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error uploading file to Kinovi: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(upload_result.public_url)
|
||||
|
||||
+4
-4
@@ -9,7 +9,7 @@ use tokens::tokens::characters::CharacterToken;
|
||||
use mysql_queries::queries::characters::delete_character::delete_character;
|
||||
use mysql_queries::queries::characters::get_character_by_token_including_deleted::get_character_by_token_including_deleted;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -32,7 +32,7 @@ pub async fn delete_character_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<DeleteCharacterPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<DeleteCharacterResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<DeleteCharacterResponse>, CommonWebError> {
|
||||
|
||||
let character_token = &path.character_token;
|
||||
|
||||
@@ -55,7 +55,7 @@ pub async fn delete_character_handler(
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
warn!("Character not found: {}", character_token);
|
||||
AdvancedCommonWebError::NotFound
|
||||
CommonWebError::NotFound
|
||||
})?;
|
||||
|
||||
// Already deleted — return success idempotently.
|
||||
@@ -76,7 +76,7 @@ pub async fn delete_character_handler(
|
||||
"User {} attempted to delete character {} owned by {:?}",
|
||||
user_token, character_token, character.maybe_creator_user_token,
|
||||
);
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
// --- Soft delete ---
|
||||
|
||||
+7
-7
@@ -10,7 +10,7 @@ use mysql_queries::queries::characters::update_character_name_and_description::u
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::update_character::update_character::{update_character, UpdateCharacterArgs};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -32,7 +32,7 @@ pub async fn edit_character_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<EditCharacterRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<EditCharacterResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<EditCharacterResponse>, CommonWebError> {
|
||||
|
||||
// --- Auth ---
|
||||
|
||||
@@ -53,7 +53,7 @@ pub async fn edit_character_handler(
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
warn!("Character not found: {}", request.token);
|
||||
AdvancedCommonWebError::NotFound
|
||||
CommonWebError::NotFound
|
||||
})?;
|
||||
|
||||
// --- Ownership check ---
|
||||
@@ -65,7 +65,7 @@ pub async fn edit_character_handler(
|
||||
|
||||
if !is_owner && !is_mod {
|
||||
warn!("User {} tried to edit character {} they don't own", user_token, request.token);
|
||||
return Err(AdvancedCommonWebError::NotFound);
|
||||
return Err(CommonWebError::NotFound);
|
||||
}
|
||||
|
||||
// --- Determine what to update ---
|
||||
@@ -114,7 +114,7 @@ pub async fn edit_character_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error updating character on Kinovi: {:?}", err);
|
||||
AdvancedCommonWebError::from_error_with_message(
|
||||
CommonWebError::from_error_with_message(
|
||||
"Error Updating Kinovi Character API".to_string(),
|
||||
err,
|
||||
)
|
||||
@@ -166,7 +166,7 @@ fn resolve_name_update(updated_name: &Option<String>) -> Option<String> {
|
||||
/// Determine the description update type.
|
||||
fn resolve_description_update(
|
||||
updated_description: &Option<String>,
|
||||
) -> Result<DescriptionUpdateType, AdvancedCommonWebError> {
|
||||
) -> Result<DescriptionUpdateType, CommonWebError> {
|
||||
let desc = match updated_description.as_ref() {
|
||||
None => return Ok(DescriptionUpdateType::NoUpdate),
|
||||
Some(d) => d,
|
||||
@@ -176,7 +176,7 @@ fn resolve_description_update(
|
||||
if trimmed.is_empty() {
|
||||
Ok(DescriptionUpdateType::Nullify)
|
||||
} else if trimmed.len() > CHARACTER_MAX_DESCRIPTION_LENGTH {
|
||||
Err(AdvancedCommonWebError::BadInputWithSimpleMessage(
|
||||
Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
format!("Description exceeds maximum length of {} characters.", CHARACTER_MAX_DESCRIPTION_LENGTH),
|
||||
))
|
||||
} else {
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@ use mysql_queries::queries::media_files::get::batch_get_media_files_by_tokens::{
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
@@ -37,7 +37,7 @@ pub async fn get_character_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<GetCharacterPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<GetCharacterResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<GetCharacterResponse>, CommonWebError> {
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
@@ -45,7 +45,7 @@ pub async fn get_character_handler(
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
warn!("Character not found: {}", path.character_token);
|
||||
AdvancedCommonWebError::NotFound
|
||||
CommonWebError::NotFound
|
||||
})?;
|
||||
|
||||
// --- Resolve media links ---
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ use mysql_queries::queries::characters::list_active_characters_for_user::list_ac
|
||||
use mysql_queries::queries::media_files::get::batch_get_media_files_by_tokens::{batch_get_media_files_by_tokens_with_connection, MediaFilesByTokensRecord};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
@@ -35,7 +35,7 @@ pub async fn list_characters_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<ListCharactersQuery>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListCharactersResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListCharactersResponse>, CommonWebError> {
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
|
||||
+3
-3
@@ -45,7 +45,7 @@ pub async fn get_session_credits_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error acquiring MySQL connection: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
@@ -54,7 +54,7 @@ pub async fn get_session_credits_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_token = match maybe_user_session {
|
||||
@@ -68,7 +68,7 @@ pub async fn get_session_credits_handler(
|
||||
&mut mysql_connection,
|
||||
).await.map_err(|err| {
|
||||
error!("Error finding primary wallet for user: {:?}, error: {:?}", user_token, err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let free_credits = 0u64;
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
use actix_web::HttpResponse;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
|
||||
/// Dev-only endpoint that unconditionally returns a `BadInputWithSimpleMessage`
|
||||
/// so we can exercise error paths (paging, response shape, etc.) end-to-end.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Dev",
|
||||
path = "/v1/dev/basic_input_error",
|
||||
responses(
|
||||
(status = 400, description = "Always returns this — that's the point"),
|
||||
),
|
||||
)]
|
||||
pub async fn dev_basic_input_error_handler() -> Result<HttpResponse, CommonWebError> {
|
||||
Err(CommonWebError::BadInputWithSimpleMessage("testing".to_string()))
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
use actix_web::HttpResponse;
|
||||
use serde_derive::Serialize;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
|
||||
/// Dev-only endpoint that unconditionally returns a `BadInputTailoredResponse`
|
||||
/// carrying a per-field validation payload, so we can exercise the tailored
|
||||
/// 400 response shape end-to-end.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Dev",
|
||||
path = "/v1/dev/input_error",
|
||||
responses(
|
||||
(status = 400, description = "Always returns this — that's the point"),
|
||||
),
|
||||
)]
|
||||
pub async fn dev_input_error_handler() -> Result<HttpResponse, CommonWebError> {
|
||||
let body = BadInputError {
|
||||
fields: vec![
|
||||
BadFieldMessage {
|
||||
field_name: "username".to_string(),
|
||||
message: "username is taken".to_string(),
|
||||
max_length: Some(32),
|
||||
min_length: Some(3),
|
||||
},
|
||||
BadFieldMessage {
|
||||
field_name: "email".to_string(),
|
||||
message: "email is invalid".to_string(),
|
||||
max_length: None,
|
||||
min_length: None,
|
||||
},
|
||||
BadFieldMessage {
|
||||
field_name: "password".to_string(),
|
||||
message: "password is too short".to_string(),
|
||||
max_length: Some(128),
|
||||
min_length: Some(8),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Err(CommonWebError::bad_input_tailored_response(body))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BadInputError {
|
||||
fields: Vec<BadFieldMessage>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BadFieldMessage {
|
||||
field_name: String,
|
||||
message: String,
|
||||
max_length: Option<usize>,
|
||||
min_length: Option<usize>,
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod dev_basic_input_error_handler;
|
||||
pub mod dev_input_error_handler;
|
||||
+6
-6
@@ -60,7 +60,7 @@ pub async fn flux_2_lora_edit_image_angle_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -148,14 +148,14 @@ pub async fn flux_2_lora_edit_image_angle_handler(
|
||||
Err(err) => {
|
||||
warn!("Error calling enqueue_flux_2_lora_edit_image_angle_webhook: {:?}", err);
|
||||
refund_wallet_after_api_failure(&wallet_deduction.ledger_entry_token, &mut mysql_connection).await?;
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -167,7 +167,7 @@ pub async fn flux_2_lora_edit_image_angle_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
@@ -251,7 +251,7 @@ pub async fn flux_2_lora_edit_image_angle_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -260,7 +260,7 @@ pub async fn flux_2_lora_edit_image_angle_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Flux2LoraEditImageAngleResponse {
|
||||
|
||||
+6
-6
@@ -60,7 +60,7 @@ pub async fn qwen_edit_2511_edit_image_angle_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -149,14 +149,14 @@ pub async fn qwen_edit_2511_edit_image_angle_handler(
|
||||
Err(err) => {
|
||||
warn!("Error calling enqueue_qwen_edit_2511_edit_image_angle_webhook: {:?}", err);
|
||||
refund_wallet_after_api_failure(&wallet_deduction.ledger_entry_token, &mut mysql_connection).await?;
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -168,7 +168,7 @@ pub async fn qwen_edit_2511_edit_image_angle_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
@@ -252,7 +252,7 @@ pub async fn qwen_edit_2511_edit_image_angle_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -261,7 +261,7 @@ pub async fn qwen_edit_2511_edit_image_angle_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(QwenEdit2511EditImageAngleResponse {
|
||||
|
||||
+5
-5
@@ -50,7 +50,7 @@ pub async fn remove_image_background_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -101,7 +101,7 @@ pub async fn remove_image_background_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,13 +135,13 @@ pub async fn remove_image_background_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling remove_background_rembg_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -167,7 +167,7 @@ pub async fn remove_image_background_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+7
-7
@@ -61,7 +61,7 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -106,7 +106,7 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -188,13 +188,13 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_pro_kontext_max_edit_webook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -206,7 +206,7 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -285,7 +285,7 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -294,7 +294,7 @@ pub async fn flux_pro_kontext_max_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(FluxProKontextMaxEditImageResponse {
|
||||
|
||||
+7
-7
@@ -69,7 +69,7 @@ pub async fn gemini_25_flash_edit_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -103,7 +103,7 @@ pub async fn gemini_25_flash_edit_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -176,13 +176,13 @@ pub async fn gemini_25_flash_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gpt_image_1_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -194,7 +194,7 @@ pub async fn gemini_25_flash_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -276,7 +276,7 @@ pub async fn gemini_25_flash_edit_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -285,7 +285,7 @@ pub async fn gemini_25_flash_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Gemini25FlashEditImageResponse {
|
||||
|
||||
+7
-7
@@ -71,7 +71,7 @@ pub async fn gpt_image_1_edit_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -105,7 +105,7 @@ pub async fn gpt_image_1_edit_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -201,13 +201,13 @@ pub async fn gpt_image_1_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gpt_image_1_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -219,7 +219,7 @@ pub async fn gpt_image_1_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -308,7 +308,7 @@ pub async fn gpt_image_1_edit_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -317,7 +317,7 @@ pub async fn gpt_image_1_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GptImage1EditImageResponse {
|
||||
|
||||
+7
-7
@@ -64,7 +64,7 @@ pub async fn qwen_edit_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -99,7 +99,7 @@ pub async fn qwen_edit_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,13 +181,13 @@ pub async fn qwen_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_qwen_image_edit_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -199,7 +199,7 @@ pub async fn qwen_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -281,7 +281,7 @@ pub async fn qwen_edit_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -290,7 +290,7 @@ pub async fn qwen_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(QwenEditImageResponse {
|
||||
|
||||
+7
-7
@@ -60,7 +60,7 @@ pub async fn seededit_3_edit_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -98,7 +98,7 @@ pub async fn seededit_3_edit_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -149,13 +149,13 @@ pub async fn seededit_3_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_seededit_v3_image_edit_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -167,7 +167,7 @@ pub async fn seededit_3_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -238,7 +238,7 @@ pub async fn seededit_3_edit_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -247,7 +247,7 @@ pub async fn seededit_3_edit_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(SeedEdit3EditImageResponse {
|
||||
|
||||
+7
-7
@@ -60,7 +60,7 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -98,7 +98,7 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -185,13 +185,13 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_dev_juggernaut_infill_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -203,7 +203,7 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -285,7 +285,7 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -294,7 +294,7 @@ pub async fn flux_dev_juggernaut_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(FluxDevJuggernautInpaintImageResponse {
|
||||
|
||||
+7
-7
@@ -60,7 +60,7 @@ pub async fn flux_pro_1_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -98,7 +98,7 @@ pub async fn flux_pro_1_inpaint_image_handler(
|
||||
Ok(files) => files,
|
||||
Err(err) => {
|
||||
error!("Error getting media files by tokens: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -185,13 +185,13 @@ pub async fn flux_pro_1_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_pro_1_infill_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -203,7 +203,7 @@ pub async fn flux_pro_1_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -285,7 +285,7 @@ pub async fn flux_pro_1_inpaint_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -294,7 +294,7 @@ pub async fn flux_pro_1_inpaint_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(FluxPro1InpaintImageResponse {
|
||||
|
||||
+7
-7
@@ -64,7 +64,7 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -158,7 +158,7 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_bytedance_seedream_v5_lite_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -213,14 +213,14 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_bytedance_seedream_v5_lite_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -232,7 +232,7 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_aspect_ratio = request.image_size.map(|size| match size {
|
||||
@@ -330,7 +330,7 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -339,7 +339,7 @@ pub async fn bytedance_seedream_5_lite_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(BytedanceSeedream5LiteMultiFunctionImageGenResponse {
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -175,7 +175,7 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_bytedance_seedream_v4_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -235,14 +235,14 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_bytedance_seedream_v4_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -254,7 +254,7 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -351,7 +351,7 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -360,7 +360,7 @@ pub async fn bytedance_seedream_v4_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(BytedanceSeedreamV4MultiFunctionImageGenResponse {
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -174,7 +174,7 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_bytedance_seedream_v4p5_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -233,14 +233,14 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_bytedance_seedream_v4p5_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -252,7 +252,7 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -348,7 +348,7 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -357,7 +357,7 @@ pub async fn bytedance_seedream_v4p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(BytedanceSeedreamV4p5MultiFunctionImageGenResponse {
|
||||
|
||||
+7
-7
@@ -76,7 +76,7 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -223,7 +223,7 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gpt_image_1p5_image_edit_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -289,14 +289,14 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gpt_image_1p5_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -308,7 +308,7 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -399,7 +399,7 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -408,7 +408,7 @@ pub async fn gpt_image_1p5_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GptImage1p5MultiFunctionImageGenResponse {
|
||||
|
||||
+7
-7
@@ -64,7 +64,7 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -169,7 +169,7 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_nano_banana_2_edit_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -236,14 +236,14 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_nano_banana_2_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -255,7 +255,7 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_aspect_ratio = match request.aspect_ratio {
|
||||
@@ -360,7 +360,7 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -369,7 +369,7 @@ pub async fn nano_banana_2_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(NanaBanana2MultiFunctionImageGenResponse {
|
||||
|
||||
+7
-7
@@ -79,7 +79,7 @@ pub async fn nano_banana_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -173,7 +173,7 @@ pub async fn nano_banana_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gemini_25_flash_edit_webhook : {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -230,14 +230,14 @@ pub async fn nano_banana_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gemini_25_flash_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -249,7 +249,7 @@ pub async fn nano_banana_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -348,7 +348,7 @@ pub async fn nano_banana_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -357,7 +357,7 @@ pub async fn nano_banana_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(NanoBananaMultiFunctionImageGenResponse {
|
||||
|
||||
+7
-7
@@ -77,7 +77,7 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -179,7 +179,7 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_nano_banana_pro_image_edit_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -244,14 +244,14 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_nano_banana_pro_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -263,7 +263,7 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -369,7 +369,7 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -378,7 +378,7 @@ pub async fn nano_banana_pro_multi_function_image_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(NanoBananaProMultiFunctionImageGenResponse {
|
||||
|
||||
+9
-9
@@ -62,7 +62,7 @@ pub async fn generate_flux_1_dev_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -169,13 +169,13 @@ pub async fn generate_flux_1_dev_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_1_dev_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -187,7 +187,7 @@ pub async fn generate_flux_1_dev_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -258,7 +258,7 @@ pub async fn generate_flux_1_dev_text_to_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -267,7 +267,7 @@ pub async fn generate_flux_1_dev_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateFlux1DevTextToImageResponse {
|
||||
@@ -295,7 +295,7 @@ async fn insert_mock_failure_job(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
@@ -346,7 +346,7 @@ async fn insert_mock_failure_job(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting mock failure job: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -355,7 +355,7 @@ async fn insert_mock_failure_job(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing mock failure transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateFlux1DevTextToImageResponse {
|
||||
|
||||
+6
-6
@@ -56,7 +56,7 @@ pub async fn generate_flux_1_schnell_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -149,13 +149,13 @@ pub async fn generate_flux_1_schnell_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_1_schnell_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -167,7 +167,7 @@ pub async fn generate_flux_1_schnell_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -238,7 +238,7 @@ pub async fn generate_flux_1_schnell_text_to_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -247,7 +247,7 @@ pub async fn generate_flux_1_schnell_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateFlux1SchnellTextToImageResponse {
|
||||
|
||||
+6
-6
@@ -62,7 +62,7 @@ pub async fn generate_flux_pro_11_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -137,13 +137,13 @@ pub async fn generate_flux_pro_11_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_pro_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -155,7 +155,7 @@ pub async fn generate_flux_pro_11_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -228,7 +228,7 @@ pub async fn generate_flux_pro_11_text_to_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -237,7 +237,7 @@ pub async fn generate_flux_pro_11_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateFluxPro11TextToImageResponse {
|
||||
|
||||
+6
-6
@@ -62,7 +62,7 @@ pub async fn generate_flux_pro_11_ultra_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -141,13 +141,13 @@ pub async fn generate_flux_pro_11_ultra_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_flux_pro_ultra_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -159,7 +159,7 @@ pub async fn generate_flux_pro_11_ultra_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -236,7 +236,7 @@ pub async fn generate_flux_pro_11_ultra_text_to_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -245,7 +245,7 @@ pub async fn generate_flux_pro_11_ultra_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateFluxPro11UltraTextToImageResponse {
|
||||
|
||||
+6
-6
@@ -68,7 +68,7 @@ pub async fn generate_gpt_image_1_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -153,13 +153,13 @@ pub async fn generate_gpt_image_1_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_gpt_image_1_text_to_image_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -171,7 +171,7 @@ pub async fn generate_gpt_image_1_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -242,7 +242,7 @@ pub async fn generate_gpt_image_1_text_to_image_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -251,7 +251,7 @@ pub async fn generate_gpt_image_1_text_to_image_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateGptImage1TextToImageResponse {
|
||||
|
||||
+7
-7
@@ -58,7 +58,7 @@ pub async fn generate_hunyuan_2_0_image_to_3d_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -109,7 +109,7 @@ pub async fn generate_hunyuan_2_0_image_to_3d_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,13 +143,13 @@ pub async fn generate_hunyuan_2_0_image_to_3d_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_hunyuan2_image_to_3d_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -158,7 +158,7 @@ pub async fn generate_hunyuan_2_0_image_to_3d_handler(
|
||||
|
||||
let mut transaction = mysql_connection.begin().await.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// Insert prompt record if we have a prompt
|
||||
@@ -233,13 +233,13 @@ pub async fn generate_hunyuan_2_0_image_to_3d_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
let _r = transaction.commit().await.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateHunyuan20ImageTo3dResponse {
|
||||
|
||||
+7
-7
@@ -57,7 +57,7 @@ pub async fn generate_hunyuan_2_1_image_to_3d_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -108,7 +108,7 @@ pub async fn generate_hunyuan_2_1_image_to_3d_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -142,13 +142,13 @@ pub async fn generate_hunyuan_2_1_image_to_3d_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_hunyuan_3d_2_1_image_to_3d_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -157,7 +157,7 @@ pub async fn generate_hunyuan_2_1_image_to_3d_handler(
|
||||
|
||||
let mut transaction = mysql_connection.begin().await.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// Insert prompt record if we have a prompt
|
||||
@@ -232,13 +232,13 @@ pub async fn generate_hunyuan_2_1_image_to_3d_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
let _r = transaction.commit().await.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateHunyuan21ImageTo3dResponse {
|
||||
|
||||
+8
-8
@@ -72,7 +72,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -196,7 +196,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
"Error calling enqueue_hunyuan3d_v3_text_to_3d_webhook: {:?}",
|
||||
err
|
||||
);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
"Error calling enqueue_hunyuan3d_v3_sketch_to_3d_webhook: {:?}",
|
||||
err
|
||||
);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
"Error calling enqueue_hunyuan3d_v3_image_to_3d_webhook: {:?}",
|
||||
err
|
||||
);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
|
||||
let external_job_id = fal_result.request_id.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -303,7 +303,7 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
|
||||
let mut transaction = mysql_connection.begin().await.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// Insert prompt record if we have a prompt
|
||||
@@ -403,13 +403,13 @@ pub async fn generate_hunyuan3d_v3_multi_function_object_handler(
|
||||
"Error inserting generic inference job for FAL queue: {:?}",
|
||||
err
|
||||
);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
let _r = transaction.commit().await.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Hunyuan3dV3MultiFunctionObjectGenResponse {
|
||||
|
||||
+15
-15
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error;
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
@@ -48,18 +48,18 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<GenerateWorldlabsMarble0p1MiniSplatRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<GenerateWorldlabsMarble0p1MiniSplatResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<GenerateWorldlabsMarble0p1MiniSplatResponse>, CommonWebError> {
|
||||
|
||||
payments_error_test(&request.prompt.as_deref().unwrap_or(""))?;
|
||||
|
||||
if request.image_media_file_token.is_none() && request.prompt.is_none() {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"At least one of image_media_file_token or prompt must be provided".to_string()
|
||||
));
|
||||
}
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
@@ -72,7 +72,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
AdvancedCommonWebError::from(e)
|
||||
CommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -82,7 +82,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
let user_token = match maybe_user_session.as_ref() {
|
||||
Some(session) => &session.user_token,
|
||||
None => {
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
let apriori_job_token = InferenceJobToken::generate();
|
||||
@@ -126,7 +126,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
let cdn_url = image_urls.get(image_token)
|
||||
.ok_or_else(|| {
|
||||
warn!("Image token not found in lookup results: {:?}", image_token);
|
||||
AdvancedCommonWebError::server_error_with_message("Image token was not found in lookup results")
|
||||
CommonWebError::server_error_with_message("Image token was not found in lookup results")
|
||||
})?;
|
||||
|
||||
WorldPrompt::Image {
|
||||
@@ -170,7 +170,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
@@ -240,7 +240,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for WorldLabs queue: {:?}", err);
|
||||
return Err(AdvancedCommonWebError::from_error(err));
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -249,7 +249,7 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateWorldlabsMarble0p1MiniSplatResponse {
|
||||
@@ -258,16 +258,16 @@ pub async fn generate_worldlabs_marble_0p1_mini_splat_handler(
|
||||
}))
|
||||
}
|
||||
|
||||
fn classify_worldlabs_error(err: WorldLabsError) -> AdvancedCommonWebError {
|
||||
fn classify_worldlabs_error(err: WorldLabsError) -> CommonWebError {
|
||||
if let WorldLabsError::ApiSpecific(WorldLabsSpecificApiError::NsfwContentPolicyRejected { message }) = &err {
|
||||
return AdvancedCommonWebError::ContentPolicyRejectedWithMessage(
|
||||
return CommonWebError::ContentPolicyRejectedWithMessage(
|
||||
message.clone().unwrap_or_else(|| "Content rejected by policy".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
if err.is_403_forbidden() {
|
||||
return AdvancedCommonWebError::Forbidden;
|
||||
return CommonWebError::Forbidden;
|
||||
}
|
||||
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
}
|
||||
|
||||
+15
-15
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error;
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
@@ -48,18 +48,18 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<GenerateWorldlabsMarble0p1PlusSplatRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<GenerateWorldlabsMarble0p1PlusSplatResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<GenerateWorldlabsMarble0p1PlusSplatResponse>, CommonWebError> {
|
||||
|
||||
payments_error_test(&request.prompt.as_deref().unwrap_or(""))?;
|
||||
|
||||
if request.image_media_file_token.is_none() && request.prompt.is_none() {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
"At least one of image_media_file_token or prompt must be provided".to_string()
|
||||
));
|
||||
}
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
@@ -72,7 +72,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
AdvancedCommonWebError::from(e)
|
||||
CommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -82,7 +82,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
let user_token = match maybe_user_session.as_ref() {
|
||||
Some(session) => &session.user_token,
|
||||
None => {
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
let apriori_job_token = InferenceJobToken::generate();
|
||||
@@ -126,7 +126,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
let cdn_url = image_urls.get(image_token)
|
||||
.ok_or_else(|| {
|
||||
warn!("Image token not found in lookup results: {:?}", image_token);
|
||||
AdvancedCommonWebError::server_error_with_message("Image token no found in lookup result")
|
||||
CommonWebError::server_error_with_message("Image token no found in lookup result")
|
||||
})?;
|
||||
|
||||
WorldPrompt::Image {
|
||||
@@ -170,7 +170,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
@@ -240,7 +240,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for WorldLabs queue: {:?}", err);
|
||||
return Err(AdvancedCommonWebError::from_error(err));
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -249,7 +249,7 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateWorldlabsMarble0p1PlusSplatResponse {
|
||||
@@ -258,17 +258,17 @@ pub async fn generate_worldlabs_marble_0p1_plus_splat_handler(
|
||||
}))
|
||||
}
|
||||
|
||||
fn classify_worldlabs_error(err: WorldLabsError) -> AdvancedCommonWebError {
|
||||
fn classify_worldlabs_error(err: WorldLabsError) -> CommonWebError {
|
||||
|
||||
if let WorldLabsError::ApiSpecific(WorldLabsSpecificApiError::NsfwContentPolicyRejected { message }) = &err {
|
||||
return AdvancedCommonWebError::ContentPolicyRejectedWithMessage(
|
||||
return CommonWebError::ContentPolicyRejectedWithMessage(
|
||||
message.clone().unwrap_or_else(|| "Content rejected by policy".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
if err.is_403_forbidden() {
|
||||
return AdvancedCommonWebError::Forbidden;
|
||||
return CommonWebError::Forbidden;
|
||||
}
|
||||
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
}
|
||||
|
||||
+19
-19
@@ -34,7 +34,7 @@ use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPrompt
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::state::server_state::ServerState;
|
||||
@@ -59,7 +59,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<BeebleSwitchXEditVideoRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<BeebleSwitchXEditVideoResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<BeebleSwitchXEditVideoResponse>, CommonWebError> {
|
||||
|
||||
payments_error_test(&request.prompt.as_deref().unwrap_or(""))?;
|
||||
|
||||
@@ -73,12 +73,12 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
AdvancedCommonWebError::from(e)
|
||||
CommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let user_token = match maybe_user_session.as_ref() {
|
||||
Some(session) => &session.user_token,
|
||||
None => return Err(AdvancedCommonWebError::NotAuthorized),
|
||||
None => return Err(CommonWebError::NotAuthorized),
|
||||
};
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -89,18 +89,18 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
|
||||
let source_video_media_token = request.source_video_media_token.as_ref()
|
||||
.ok_or_else(|| {
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("source_video_media_token is required".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("source_video_media_token is required".to_string())
|
||||
})?;
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(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);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// ==================== LOOKUP MEDIA FILES (BATCH) ==================== //
|
||||
@@ -118,13 +118,13 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
&tokens_to_lookup,
|
||||
).await.map_err(|err| {
|
||||
warn!("Error looking up media files: {:?}", err);
|
||||
AdvancedCommonWebError::from(err)
|
||||
CommonWebError::from(err)
|
||||
})?;
|
||||
|
||||
let source_video_cdn_url = cdn_url_map.get(source_video_media_token)
|
||||
.ok_or_else(|| {
|
||||
warn!("Source video media file not found: {:?}", source_video_media_token);
|
||||
AdvancedCommonWebError::NotFound
|
||||
CommonWebError::NotFound
|
||||
})?
|
||||
.clone();
|
||||
|
||||
@@ -134,7 +134,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
warn!("Reference image media file not found: {:?}", ref_token);
|
||||
AdvancedCommonWebError::NotFound
|
||||
CommonWebError::NotFound
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
@@ -148,7 +148,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
let video_bytes = http_download_url_to_bytes(&source_video_cdn_url).await
|
||||
.map_err(|err| {
|
||||
error!("Failed to download source video: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Downloaded source video: {} bytes", video_bytes.len());
|
||||
@@ -170,7 +170,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
beeble_api_key, &video_filename, video_content_type, video_bytes.to_vec(),
|
||||
).await.map_err(|err| {
|
||||
error!("Beeble video upload failed: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Source video uploaded to Beeble: {}", video_upload.beeble_uri);
|
||||
@@ -181,7 +181,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
let image_bytes = http_download_url_to_bytes(ref_cdn_url).await
|
||||
.map_err(|err| {
|
||||
error!("Failed to download reference image: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Downloaded reference image: {} bytes", image_bytes.len());
|
||||
@@ -193,7 +193,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
beeble_api_key, &image_filename, "image/jpeg", image_bytes.to_vec(),
|
||||
).await.map_err(|err| {
|
||||
error!("Beeble image upload failed: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Reference image uploaded to Beeble: {}", image_upload.beeble_uri);
|
||||
@@ -227,13 +227,13 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
detected_frames,
|
||||
ref message,
|
||||
}) => {
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage(format!(
|
||||
CommonWebError::BadInputWithSimpleMessage(format!(
|
||||
"Video has too many frames: {} (max: {}); {}",
|
||||
detected_frames, max_frames, message
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
@@ -247,7 +247,7 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
|
||||
let mut transaction = mysql_connection.begin().await.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// -- Prompt --
|
||||
@@ -330,13 +330,13 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
error!("Error inserting inference job: {:?}", err);
|
||||
return Err(AdvancedCommonWebError::from_error(err));
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
transaction.commit().await.map_err(|err| {
|
||||
error!("Error committing transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(BeebleSwitchXEditVideoResponse {
|
||||
|
||||
+6
-6
@@ -64,7 +64,7 @@ pub async fn generate_kling_1_6_pro_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -192,13 +192,13 @@ pub async fn generate_kling_1_6_pro_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_16_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -210,7 +210,7 @@ pub async fn generate_kling_1_6_pro_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -277,7 +277,7 @@ pub async fn generate_kling_1_6_pro_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -286,7 +286,7 @@ pub async fn generate_kling_1_6_pro_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateKling16ProImageToVideoResponse {
|
||||
|
||||
+7
-7
@@ -63,7 +63,7 @@ pub async fn generate_kling_2_1_master_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -111,7 +111,7 @@ pub async fn generate_kling_2_1_master_video_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -181,13 +181,13 @@ pub async fn generate_kling_2_1_master_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_21_master_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -199,7 +199,7 @@ pub async fn generate_kling_2_1_master_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -264,7 +264,7 @@ pub async fn generate_kling_2_1_master_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -273,7 +273,7 @@ pub async fn generate_kling_2_1_master_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateKling21MasterImageToVideoResponse {
|
||||
|
||||
+6
-6
@@ -69,7 +69,7 @@ pub async fn generate_kling_2_1_pro_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -197,13 +197,13 @@ pub async fn generate_kling_2_1_pro_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_21_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -215,7 +215,7 @@ pub async fn generate_kling_2_1_pro_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -280,7 +280,7 @@ pub async fn generate_kling_2_1_pro_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -289,7 +289,7 @@ pub async fn generate_kling_2_1_pro_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateKling21ProImageToVideoResponse {
|
||||
|
||||
+6
-6
@@ -69,7 +69,7 @@ pub async fn generate_seedance_1_0_lite_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -211,13 +211,13 @@ pub async fn generate_seedance_1_0_lite_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_seedance_1_lite_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -229,7 +229,7 @@ pub async fn generate_seedance_1_0_lite_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -298,7 +298,7 @@ pub async fn generate_seedance_1_0_lite_image_to_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,7 +307,7 @@ pub async fn generate_seedance_1_0_lite_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateSeedance10LiteImageToVideoResponse {
|
||||
|
||||
+6
-6
@@ -66,7 +66,7 @@ pub async fn generate_seedance_1_0_pro_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -185,13 +185,13 @@ pub async fn generate_seedance_1_0_pro_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_seedance_1_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -203,7 +203,7 @@ pub async fn generate_seedance_1_0_pro_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -272,7 +272,7 @@ pub async fn generate_seedance_1_0_pro_image_to_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -281,7 +281,7 @@ pub async fn generate_seedance_1_0_pro_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateSeedance10ProImageToVideoResponse {
|
||||
|
||||
+7
-7
@@ -70,7 +70,7 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -118,7 +118,7 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -186,13 +186,13 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_2_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -204,7 +204,7 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -269,7 +269,7 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -278,7 +278,7 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateVeo2ImageToVideoResponse {
|
||||
|
||||
+7
-7
@@ -62,7 +62,7 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -110,7 +110,7 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -190,13 +190,13 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3_fast_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -208,7 +208,7 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -274,7 +274,7 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -283,7 +283,7 @@ pub async fn generate_veo_3_fast_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateVeo3FastImageToVideoResponse {
|
||||
|
||||
+7
-7
@@ -65,7 +65,7 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -113,7 +113,7 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -194,13 +194,13 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -212,7 +212,7 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -278,7 +278,7 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -287,7 +287,7 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateVeo3ImageToVideoResponse {
|
||||
|
||||
+7
-7
@@ -74,7 +74,7 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -193,7 +193,7 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_v2p5_turbo_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -242,14 +242,14 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_v2p5_turbo_pro_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -261,7 +261,7 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -357,7 +357,7 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -366,7 +366,7 @@ pub async fn kling_2p5_turbo_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Kling2p5TurboProMultiFunctionVideoGenResponse {
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -190,7 +190,7 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_v2p6_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -239,14 +239,14 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_v2p6_pro_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -258,7 +258,7 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -354,7 +354,7 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -363,7 +363,7 @@ pub async fn kling_2p6_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Kling2p6ProMultiFunctionVideoGenResponse {
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -192,7 +192,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_3p0_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -233,14 +233,14 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_3p0_pro_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -252,7 +252,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -359,7 +359,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -368,7 +368,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Kling3p0ProMultiFunctionVideoGenResponse {
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -192,7 +192,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_3p0_standard_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -233,14 +233,14 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_kling_3p0_standard_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -252,7 +252,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -359,7 +359,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -368,7 +368,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Kling3p0StandardMultiFunctionVideoGenResponse {
|
||||
|
||||
+7
-7
@@ -63,7 +63,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -180,7 +180,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_seedance_1p5_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -220,14 +220,14 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_seedance_1p5_pro_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -239,7 +239,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -346,7 +346,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -355,7 +355,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Seedance1p5ProMultiFunctionVideoGenResponse {
|
||||
|
||||
+21
-21
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::billing::wallets::attempt_wallet_deduction::attempt_wallet_deduction_else_common_web_error;
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoint_helpers::refund_wallet_after_api_failure::refund_wallet_after_api_failure;
|
||||
use crate::http_server::endpoints::generate::common::payments_error_test::payments_error_test;
|
||||
use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags;
|
||||
@@ -79,7 +79,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<Seedance2p0MultiFunctionVideoGenRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<Seedance2p0MultiFunctionVideoGenResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<Seedance2p0MultiFunctionVideoGenResponse>, CommonWebError> {
|
||||
|
||||
payments_error_test(&request.prompt.as_deref().unwrap_or(""))?;
|
||||
|
||||
@@ -97,20 +97,20 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
AdvancedCommonWebError::from(e)
|
||||
CommonWebError::from(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
Some(session) => session,
|
||||
None => {
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
let user_token = &user_session.user_token;
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(reason));
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
// --- Collect all media tokens to look up ---
|
||||
@@ -158,7 +158,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
CommonWebError::BadInputWithSimpleMessage("repeated idempotency token".to_string())
|
||||
})?;
|
||||
|
||||
// --- Calculate cost and charge wallet upfront (before uploads) ---
|
||||
@@ -301,7 +301,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the prompt insert fails.
|
||||
@@ -437,7 +437,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
Err(err) => {
|
||||
warn!("Error inserting seedance2pro inference job (order_id={}): {:?}", order_id, err);
|
||||
if i == 0 {
|
||||
return Err(AdvancedCommonWebError::from_error(err));
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,7 +445,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
|
||||
let first_job_token = all_job_tokens.first().cloned().ok_or_else(|| {
|
||||
error!("No inference job token was created");
|
||||
AdvancedCommonWebError::server_error_with_message("No inference job token was created")
|
||||
CommonWebError::server_error_with_message("No inference job token was created")
|
||||
})?;
|
||||
|
||||
transaction
|
||||
@@ -453,7 +453,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Seedance2p0MultiFunctionVideoGenResponse {
|
||||
@@ -529,14 +529,14 @@ async fn upload_and_generate(
|
||||
batch_count: KinoviBatchCount,
|
||||
duration_seconds: u8,
|
||||
kinovi_character_ids: Option<Vec<String>>,
|
||||
) -> Result<SeedanceGenerationResult, AdvancedCommonWebError> {
|
||||
) -> Result<SeedanceGenerationResult, CommonWebError> {
|
||||
|
||||
// --- Upload files to seedance2pro CDN ---
|
||||
|
||||
let start_frame_url = match request.start_frame_media_token.as_ref() {
|
||||
None => None,
|
||||
Some(token) => match file_urls_by_token.get(token) {
|
||||
None => return Err(AdvancedCommonWebError::BadInputWithSimpleMessage("Start frame media not found.".to_string())),
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("Start frame media not found.".to_string())),
|
||||
Some(url) => Some(upload_to_seedance2pro(session, url).await?),
|
||||
}
|
||||
};
|
||||
@@ -544,7 +544,7 @@ async fn upload_and_generate(
|
||||
let end_frame_url = match request.end_frame_media_token.as_ref() {
|
||||
None => None,
|
||||
Some(token) => match file_urls_by_token.get(token) {
|
||||
None => return Err(AdvancedCommonWebError::BadInputWithSimpleMessage("End frame media not found.".to_string())),
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage("End frame media not found.".to_string())),
|
||||
Some(url) => Some(upload_to_seedance2pro(session, url).await?),
|
||||
}
|
||||
};
|
||||
@@ -614,7 +614,7 @@ async fn upload_and_generate(
|
||||
let gen_response = generate_video(video_gen_args).await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling seedance2pro generate_video: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(SeedanceGenerationResult {
|
||||
@@ -630,7 +630,7 @@ async fn upload_and_generate(
|
||||
async fn resolve_kinovi_character_ids(
|
||||
maybe_tokens: Option<&[CharacterToken]>,
|
||||
connection: &mut sqlx::pool::PoolConnection<MySql>,
|
||||
) -> Result<Option<Vec<String>>, AdvancedCommonWebError> {
|
||||
) -> Result<Option<Vec<String>>, CommonWebError> {
|
||||
let tokens = match maybe_tokens {
|
||||
None => return Ok(None),
|
||||
Some(tokens) if tokens.is_empty() => return Ok(None),
|
||||
@@ -668,7 +668,7 @@ async fn upload_reference_tokens_to_seedance2pro(
|
||||
file_urls_by_token: &HashMap<MediaFileToken, Url>,
|
||||
maybe_tokens: Option<&[MediaFileToken]>,
|
||||
label: &str,
|
||||
) -> Result<Option<Vec<String>>, AdvancedCommonWebError> {
|
||||
) -> Result<Option<Vec<String>>, CommonWebError> {
|
||||
let tokens = match maybe_tokens {
|
||||
None => return Ok(None),
|
||||
Some(tokens) if tokens.is_empty() => return Ok(None),
|
||||
@@ -679,7 +679,7 @@ async fn upload_reference_tokens_to_seedance2pro(
|
||||
|
||||
for token in tokens {
|
||||
match file_urls_by_token.get(token) {
|
||||
None => return Err(AdvancedCommonWebError::BadInputWithSimpleMessage(
|
||||
None => return Err(CommonWebError::BadInputWithSimpleMessage(
|
||||
format!("{} media not found: {:?}", label, token),
|
||||
)),
|
||||
Some(url) => {
|
||||
@@ -694,7 +694,7 @@ async fn upload_reference_tokens_to_seedance2pro(
|
||||
async fn upload_to_seedance2pro(
|
||||
session: &Seedance2ProSession,
|
||||
our_cdn_url: &Url,
|
||||
) -> Result<String, AdvancedCommonWebError> {
|
||||
) -> Result<String, CommonWebError> {
|
||||
let extension = extract_extension_from_url(our_cdn_url, &ExtractExtensions::All)
|
||||
.map(|ext| ext.without_period().to_string())
|
||||
.unwrap_or_else(|| "png".to_string());
|
||||
@@ -705,7 +705,7 @@ async fn upload_to_seedance2pro(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error downloading media file from CDN: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?
|
||||
.to_vec();
|
||||
|
||||
@@ -717,7 +717,7 @@ async fn upload_to_seedance2pro(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error preparing seedance2pro file upload: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let upload_result = upload_file(UploadFileArgs {
|
||||
@@ -728,7 +728,7 @@ async fn upload_to_seedance2pro(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error uploading file to seedance2pro: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(upload_result.public_url)
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn sora_2_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -204,7 +204,7 @@ pub async fn sora_2_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_sora_2_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -259,14 +259,14 @@ pub async fn sora_2_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_sora_2_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -278,7 +278,7 @@ pub async fn sora_2_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -375,7 +375,7 @@ pub async fn sora_2_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -384,7 +384,7 @@ pub async fn sora_2_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Sora2MultiFunctionVideoGenResponse {
|
||||
|
||||
+7
-7
@@ -72,7 +72,7 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -205,7 +205,7 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_sora_2_pro_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -261,14 +261,14 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_sora_2_pro_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -280,7 +280,7 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -377,7 +377,7 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -386,7 +386,7 @@ pub async fn sora_2_pro_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Sora2ProMultiFunctionVideoGenResponse {
|
||||
|
||||
+8
-8
@@ -71,7 +71,7 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -209,7 +209,7 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling veo_3p1_fast_first_last_frame_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -266,7 +266,7 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3p1_fast_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -327,14 +327,14 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3p1_fast_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -346,7 +346,7 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -443,7 +443,7 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -452,7 +452,7 @@ pub async fn veo_3p1_fast_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Veo3p1FastMultiFunctionVideoGenResponse {
|
||||
|
||||
+8
-8
@@ -71,7 +71,7 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
@@ -209,7 +209,7 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling veo_3p1_first_last_frame_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
} else {
|
||||
@@ -266,7 +266,7 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3p1_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -327,14 +327,14 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3p1_text_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::server_error_with_message("Fal request_id is None")
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
@@ -346,7 +346,7 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
@@ -443,7 +443,7 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -452,7 +452,7 @@ pub async fn veo_3p1_multi_function_video_gen_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(Veo3p1MultiFunctionVideoGenResponse {
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ pub async fn dismiss_finished_session_jobs_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Could not acquire DB pool: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = require_user_session_using_connection(
|
||||
@@ -48,7 +48,7 @@ pub async fn dismiss_finished_session_jobs_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("tts job query error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(DismissFinishedSessionJobsSuccessResponse {
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
@@ -161,7 +161,7 @@ pub async fn batch_get_inference_job_status_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<BatchGetInferenceJobStatusQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<BatchGetInferenceJobStatusSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<BatchGetInferenceJobStatusSuccessResponse>, CommonWebError> {
|
||||
|
||||
let tokens = query.tokens.iter()
|
||||
.map(|token| token.trim())
|
||||
@@ -188,14 +188,14 @@ pub async fn batch_get_inference_job_status_handler(
|
||||
).await
|
||||
.map_err(|err| {
|
||||
warn!("Batch job query error: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let mut redis = server_state.redis_pool
|
||||
.get()
|
||||
.map_err(|e| {
|
||||
warn!("Redis pool error: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
// TODO(bt,2024-04-22): Look up the extra redis statuses per item.
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
@@ -153,12 +153,12 @@ pub async fn get_inference_job_status_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<GetInferenceJobStatusPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<GetInferenceJobStatusSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<GetInferenceJobStatusSuccessResponse>, CommonWebError> {
|
||||
if path.token.as_str().trim() == "None" {
|
||||
// NB: A bunch of Python clients use our API and can fail in this manner.
|
||||
// This was a large traffic driver during the 2023-03-08 outage.
|
||||
// Presumably, it's this client: https://github.com/shards-7/fakeyou.py
|
||||
return Err(AdvancedCommonWebError::NotFound);
|
||||
return Err(CommonWebError::NotFound);
|
||||
}
|
||||
|
||||
// NB: Since this is publicly exposed, we don't query sensitive data.
|
||||
@@ -169,10 +169,10 @@ pub async fn get_inference_job_status_handler(
|
||||
|
||||
let record = match maybe_status {
|
||||
Ok(Some(record)) => record,
|
||||
Ok(None) => return Err(AdvancedCommonWebError::NotFound),
|
||||
Ok(None) => return Err(CommonWebError::NotFound),
|
||||
Err(err) => {
|
||||
warn!("Job query error for token {}: {:?}", path.token.as_str(), err);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(err));
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ pub async fn get_inference_job_status_handler(
|
||||
.get()
|
||||
.map_err(|e| {
|
||||
warn!("Redis pool error: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
// TODO(bt,2023-05-21): Make async.
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
@@ -63,7 +63,7 @@ pub async fn list_session_jobs_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<ListSessionJobsQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListSessionJobsSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListSessionJobsSuccessResponse>, CommonWebError> {
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
|
||||
@@ -72,7 +72,7 @@ pub async fn list_session_jobs_handler(
|
||||
|
||||
// ==================== USER SESSION ==================== //
|
||||
|
||||
// NB: SessionCheckerError → AdvancedCommonWebError maps payload errors (bad cookie) to 401
|
||||
// NB: SessionCheckerError → CommonWebError maps payload errors (bad cookie) to 401
|
||||
// and DB / cache errors to 500.
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -96,7 +96,7 @@ pub async fn list_session_jobs_handler(
|
||||
(None, Some(avt_token)) => SessionUser::Anonymous(avt_token),
|
||||
(None, None) => {
|
||||
// TODO(bt,2025-04-15): We should install an AVT cookie.
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,7 +111,7 @@ pub async fn list_session_jobs_handler(
|
||||
|
||||
let mut redis = server_state.redis_pool
|
||||
.get()
|
||||
.map_err(AdvancedCommonWebError::from_error)?;
|
||||
.map_err(CommonWebError::from_error)?;
|
||||
|
||||
// TODO(bt,2024-04-22): Look up the extra redis statuses per item.
|
||||
|
||||
@@ -142,7 +142,7 @@ fn records_to_response(
|
||||
records: Vec<GenericInferenceJobStatus>,
|
||||
server_environment: ServerEnvironment,
|
||||
media_domain: MediaDomain,
|
||||
) -> Result<Json<ListSessionJobsSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListSessionJobsSuccessResponse>, CommonWebError> {
|
||||
let mut records = records.into_iter()
|
||||
.map(|record| {
|
||||
db_record_to_response_payload(record, None, server_environment, media_domain)
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ pub async fn delete_media_file_handler(
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let user_session = match maybe_user_session {
|
||||
@@ -75,7 +75,7 @@ pub async fn delete_media_file_handler(
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -136,7 +136,7 @@ pub async fn delete_media_file_handler(
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
warn!("Update media_file mod approval status DB error: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
return Err(CommonWebError::from_anyhow_error(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -24,7 +24,7 @@ use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
|
||||
@@ -214,7 +214,7 @@ pub async fn batch_get_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<BatchGetMediaFilesQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<BatchGetMediaFilesSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<BatchGetMediaFilesSuccessResponse>, CommonWebError> {
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session(&http_request, &server_state.mysql_pool)
|
||||
@@ -248,7 +248,7 @@ pub async fn batch_get_media_files_handler(
|
||||
Ok(media_files) => media_files,
|
||||
Err(e) => {
|
||||
warn!("query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
@@ -217,7 +217,7 @@ pub async fn get_media_file_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<GetMediaFilePathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<GetMediaFileSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<GetMediaFileSuccessResponse>, CommonWebError> {
|
||||
let media_file_token = path.into_inner().token;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
@@ -267,7 +267,7 @@ async fn modern_media_file_lookup(
|
||||
show_deleted_results: bool,
|
||||
server_state: &ServerState,
|
||||
media_domain: MediaDomain,
|
||||
) -> Result<GetMediaFileSuccessResponse, AdvancedCommonWebError> {
|
||||
) -> Result<GetMediaFileSuccessResponse, CommonWebError> {
|
||||
|
||||
let is_mod = show_deleted_results;
|
||||
|
||||
@@ -280,9 +280,9 @@ async fn modern_media_file_lookup(
|
||||
let result = match result {
|
||||
Err(e) => {
|
||||
warn!("query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
Ok(None) => return Err(AdvancedCommonWebError::NotFound),
|
||||
Ok(None) => return Err(CommonWebError::NotFound),
|
||||
Ok(Some(result)) => result,
|
||||
};
|
||||
|
||||
@@ -330,7 +330,7 @@ async fn modern_media_file_lookup(
|
||||
public_bucket_url: bucket_url_from_media_path(&public_bucket_path, media_domain, server_state.server_environment)
|
||||
.map_err(|err| {
|
||||
warn!("error creating URL: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?,
|
||||
public_bucket_path: public_bucket_path
|
||||
.get_full_object_path_str()
|
||||
@@ -415,7 +415,7 @@ async fn emulate_media_file_with_legacy_tts_result_lookup(
|
||||
show_deleted_results: bool,
|
||||
server_state: &ServerState,
|
||||
media_domain: MediaDomain,
|
||||
) -> Result<GetMediaFileSuccessResponse, AdvancedCommonWebError> {
|
||||
) -> Result<GetMediaFileSuccessResponse, CommonWebError> {
|
||||
|
||||
let result = select_tts_result_by_token(
|
||||
&media_file_token.as_str(),
|
||||
@@ -426,9 +426,9 @@ async fn emulate_media_file_with_legacy_tts_result_lookup(
|
||||
let result = match result {
|
||||
Err(e) => {
|
||||
warn!("query error (legacy TTS): {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
Ok(None) => return Err(AdvancedCommonWebError::NotFound),
|
||||
Ok(None) => return Err(CommonWebError::NotFound),
|
||||
Ok(Some(result)) => result,
|
||||
};
|
||||
|
||||
@@ -468,7 +468,7 @@ async fn emulate_media_file_with_legacy_tts_result_lookup(
|
||||
public_bucket_url: bucket_url_from_str_path(&public_bucket_path, media_domain, server_state.server_environment)
|
||||
.map_err(|err| {
|
||||
warn!("error creating URL: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?,
|
||||
public_bucket_path,
|
||||
cover_image: MediaFileCoverImageDetails::from_legacy_token_str(&result.tts_result_token),
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details_builder::MediaFileCoverImageDetailsBuilder;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::user_details_lite_builder::UserDetailsLightBuilder;
|
||||
@@ -40,7 +40,7 @@ pub async fn list_batch_generated_redux_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ListBatchGeneratedReduxMediaFilesPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListBatchGeneratedReduxMediaFilesSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListBatchGeneratedReduxMediaFilesSuccessResponse>, CommonWebError> {
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await?;
|
||||
@@ -74,7 +74,7 @@ pub async fn list_batch_generated_redux_media_files_handler(
|
||||
Ok(batch) => batch.media_files,
|
||||
Err(e) => {
|
||||
warn!("query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -21,7 +21,7 @@ use time::ext::InstantExt;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
|
||||
@@ -197,7 +197,7 @@ pub async fn list_featured_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<ListFeaturedMediaFilesQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, AdvancedCommonWebError> {
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
let cache_start = Instant::now();
|
||||
|
||||
@@ -337,7 +337,7 @@ async fn database_lookup(
|
||||
query: &ListFeaturedMediaFilesQueryParams,
|
||||
server_state: &ServerState,
|
||||
maybe_cloudflare_header: Option<&str>,
|
||||
) -> Result<FeaturedMediaFileListPage, AdvancedCommonWebError> {
|
||||
) -> Result<FeaturedMediaFileListPage, CommonWebError> {
|
||||
|
||||
// TODO(bt,2023-12-04): Enforce real maximums and defaults
|
||||
let limit = query.page_size.unwrap_or(25);
|
||||
@@ -376,7 +376,7 @@ async fn database_lookup(
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await.map_err(|err| {
|
||||
error!("DB error: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let query_duration = Instant::now().signed_duration_since(query_start);
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
|
||||
@@ -159,7 +159,7 @@ pub async fn list_media_files_by_batch_token_handler(
|
||||
path: Path<ListMediaFilesByBatchPathInfo>,
|
||||
query: Query<ListMediaFilesByBatchQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListMediaFilesByBatchSuccessResponse>, AdvancedCommonWebError>
|
||||
) -> Result<Json<ListMediaFilesByBatchSuccessResponse>, CommonWebError>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -193,7 +193,7 @@ pub async fn list_media_files_by_batch_token_handler(
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ use mysql_queries::queries::media_files::list::list_media_files_for_user::{list_
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
|
||||
@@ -194,7 +194,7 @@ pub async fn list_media_files_for_user_handler(
|
||||
path: Path<ListMediaFilesForUserPathInfo>,
|
||||
query: Query<ListMediaFilesForUserQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListMediaFilesForUserSuccessResponse>, AdvancedCommonWebError>
|
||||
) -> Result<Json<ListMediaFilesForUserSuccessResponse>, CommonWebError>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -254,7 +254,7 @@ pub async fn list_media_files_for_user_handler(
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -20,7 +20,7 @@ use mysql_queries::queries::media_files::list::list_media_files::{list_media_fil
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
|
||||
@@ -190,7 +190,7 @@ pub async fn list_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<ListMediaFilesQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListMediaFilesSuccessResponse>, AdvancedCommonWebError>
|
||||
) -> Result<Json<ListMediaFilesSuccessResponse>, CommonWebError>
|
||||
{
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
@@ -201,7 +201,7 @@ pub async fn list_media_files_handler(
|
||||
|
||||
if !allowed_explore_media_access(maybe_user_session.as_ref()) {
|
||||
warn!("Explore media access is not permitted for user");
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
let mut is_mod = false;
|
||||
@@ -254,7 +254,7 @@ pub async fn list_media_files_handler(
|
||||
Ok(results) => results,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+5
-5
@@ -19,7 +19,7 @@ use redis::Commands;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
|
||||
@@ -136,18 +136,18 @@ const REDIS_KEY : &str = "pinned_media_files_list";
|
||||
pub async fn list_pinned_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListPinnedMediaFilesSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListPinnedMediaFilesSuccessResponse>, CommonWebError> {
|
||||
|
||||
let mut redis = server_state.redis_pool.get()
|
||||
.map_err(|err| {
|
||||
error!("Could not obtain redis: {err}");
|
||||
AdvancedCommonWebError::server_error_with_message("pinned media files error")
|
||||
CommonWebError::server_error_with_message("pinned media files error")
|
||||
})?;
|
||||
|
||||
let token_list : Option<String> = redis.get(REDIS_KEY)
|
||||
.map_err(|err| {
|
||||
error!("Could not get redis result: {err}");
|
||||
AdvancedCommonWebError::server_error_with_message("pinned media files error")
|
||||
CommonWebError::server_error_with_message("pinned media files error")
|
||||
})?;
|
||||
|
||||
let media_file_tokens = token_list
|
||||
@@ -171,7 +171,7 @@ pub async fn list_pinned_media_files_handler(
|
||||
Ok(media_files) => media_files,
|
||||
Err(e) => {
|
||||
warn!("Query error: {:?}", e);
|
||||
return Err(AdvancedCommonWebError::from_anyhow_error(e));
|
||||
return Err(CommonWebError::from_anyhow_error(e));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod billing_fakeyou;
|
||||
pub mod characters;
|
||||
pub mod comments;
|
||||
pub mod credits;
|
||||
pub mod dev;
|
||||
pub mod download_job;
|
||||
pub mod featured_items;
|
||||
pub mod generate;
|
||||
|
||||
+5
-5
@@ -13,7 +13,7 @@ use mysql_queries::queries::staff_audit_logs::insert_staff_audit_log::{
|
||||
};
|
||||
use pager::notification::notification_details_builder::NotificationDetailsBuilder;
|
||||
use pager::notification::notification_urgency::NotificationUrgency;
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{require_moderator, UseDatabase};
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -33,13 +33,13 @@ pub async fn moderation_send_alert_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<ModerationSendAlertRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModerationSendAlertResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ModerationSendAlertResponse>, CommonWebError> {
|
||||
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
AdvancedCommonWebError::NotAuthorized
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
@@ -77,7 +77,7 @@ pub async fn moderation_send_alert_handler(
|
||||
.enqueue_page(notification)
|
||||
.map_err(|err| {
|
||||
warn!("moderation_send_alert error: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// Insert staff audit log.
|
||||
@@ -91,7 +91,7 @@ pub async fn moderation_send_alert_handler(
|
||||
phantom: PhantomData,
|
||||
}).await.map_err(|err| {
|
||||
warn!("Failed to insert staff audit log: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
Ok(Json(ModerationSendAlertResponse {
|
||||
|
||||
+4
-4
@@ -14,7 +14,7 @@ use mysql_queries::queries::debug_logs::list_debug_logs_for_token::{
|
||||
use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
@@ -73,14 +73,14 @@ pub async fn moderation_list_debug_logs_for_token_handler(
|
||||
path: web::Path<ListDebugLogsPathInfo>,
|
||||
query: web::Query<ListDebugLogsQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListDebugLogsSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListDebugLogsSuccessResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
AdvancedCommonWebError::NotAuthorized
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
@@ -92,7 +92,7 @@ pub async fn moderation_list_debug_logs_for_token_handler(
|
||||
phantom: Default::default(),
|
||||
}).await.map_err(|err| {
|
||||
warn!("Error listing debug logs for token {}: {:?}", path.token, err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let debug_logs: Vec<DebugLogEntry> = rows.into_iter().map(|row| {
|
||||
|
||||
+5
-5
@@ -15,7 +15,7 @@ use tokens::tokens::non_unique::debug_logs_event_token::DebugLogEventToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
@@ -91,14 +91,14 @@ pub async fn moderation_get_job_by_token_handler(
|
||||
http_request: HttpRequest,
|
||||
path: web::Path<GetJobByTokenPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<GetJobByTokenSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<GetJobByTokenSuccessResponse>, CommonWebError> {
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
&server_state,
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
AdvancedCommonWebError::NotAuthorized
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire().await?;
|
||||
@@ -109,10 +109,10 @@ pub async fn moderation_get_job_by_token_handler(
|
||||
phantom: Default::default(),
|
||||
}).await.map_err(|err| {
|
||||
warn!("Error fetching job by token {}: {:?}", path.token, err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let job = job.ok_or(AdvancedCommonWebError::NotFound)?;
|
||||
let job = job.ok_or(CommonWebError::NotFound)?;
|
||||
|
||||
Ok(Json(GetJobByTokenSuccessResponse {
|
||||
success: true,
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ pub async fn list_user_jobs_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("list_user_jobs error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let jobs = results.into_iter().map(|row| ListUserJobsEntry {
|
||||
|
||||
+6
-6
@@ -12,7 +12,7 @@ use mysql_queries::queries::staff_audit_logs::list_staff_audit_logs::{
|
||||
use tokens::tokens::staff_audit_logs::StaffAuditLogToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
@@ -74,7 +74,7 @@ pub async fn moderator_list_staff_audit_logs_handler(
|
||||
http_request: HttpRequest,
|
||||
query: Query<ListStaffAuditLogsQueryParams>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListStaffAuditLogsSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ListStaffAuditLogsSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(
|
||||
&http_request,
|
||||
@@ -82,7 +82,7 @@ pub async fn moderator_list_staff_audit_logs_handler(
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
AdvancedCommonWebError::NotAuthorized
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
|
||||
let limit = query.limit
|
||||
@@ -96,7 +96,7 @@ pub async fn moderator_list_staff_audit_logs_handler(
|
||||
.decode_cursor_expecting_name(CURSOR_NAME, cursor_str)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to decode cursor: {:?}", err);
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage(
|
||||
CommonWebError::BadInputWithSimpleMessage(
|
||||
"Invalid cursor".to_string())
|
||||
})?;
|
||||
decoded.last_id
|
||||
@@ -111,7 +111,7 @@ pub async fn moderator_list_staff_audit_logs_handler(
|
||||
},
|
||||
).await.map_err(|err| {
|
||||
warn!("Failed to list staff audit logs: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_cursor = records.last().map(|last| {
|
||||
@@ -119,7 +119,7 @@ pub async fn moderator_list_staff_audit_logs_handler(
|
||||
.encode_last_id_cursor(CURSOR_NAME, last.id)
|
||||
}).transpose().map_err(|err| {
|
||||
warn!("Failed to encode cursor: {:?}", err);
|
||||
AdvancedCommonWebError::server_error_with_message("Failed to encode cursor")
|
||||
CommonWebError::server_error_with_message("Failed to encode cursor")
|
||||
})?;
|
||||
|
||||
let audit_logs = records.into_iter().map(|r| {
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ pub async fn moderator_list_subscribing_users_by_signup_date_handler(
|
||||
).await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_list_subscribing_users_by_signup_date error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let users = result.users.into_iter().map(|u| ModeratorListSubscribingUsersBySignupDateEntry {
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ pub async fn moderator_list_users_by_signup_date_handler(
|
||||
).await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_list_users_by_signup_date error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let users = result.users.into_iter().map(|u| ModeratorListUsersBySignupDateEntry {
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ pub async fn moderator_user_lookup_by_stripe_customer_id_handler(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_user_lookup_by_stripe_customer_id error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let users = results.into_iter().map(|row| ModeratorUserLookupByStripeCustomerIdEntry {
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ pub async fn moderator_user_lookup_handler(
|
||||
let maybe_user = maybe_result
|
||||
.map_err(|err| {
|
||||
warn!("moderator_user_lookup error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let maybe_user_details = maybe_user.map(|user| ModeratorUserLookupUserDetails {
|
||||
|
||||
+10
-10
@@ -17,7 +17,7 @@ use mysql_queries::queries::users::user::update::set_user_ban_status::{
|
||||
};
|
||||
use mysql_queries::queries::users::user_profiles::get_user_profile_by_username::get_user_profile_by_username;
|
||||
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::web_utils::user_session::require_moderator::{
|
||||
require_moderator, UseDatabase,
|
||||
};
|
||||
@@ -58,7 +58,7 @@ pub async fn moderation_ban_user_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<ModerationBanUserRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModerationBanUserSuccessResponse>, AdvancedCommonWebError> {
|
||||
) -> Result<Json<ModerationBanUserSuccessResponse>, CommonWebError> {
|
||||
|
||||
// 1. Require moderator with ban permissions.
|
||||
let user_session = require_moderator(
|
||||
@@ -67,12 +67,12 @@ pub async fn moderation_ban_user_handler(
|
||||
UseDatabase::GrabNewConnection,
|
||||
).await.map_err(|err| {
|
||||
warn!("Moderator check failed: {:?}", err);
|
||||
AdvancedCommonWebError::NotAuthorized
|
||||
CommonWebError::NotAuthorized
|
||||
})?;
|
||||
|
||||
if !user_session.can_ban_users {
|
||||
warn!("User {} is not allowed to ban users", user_session.user_token.as_str());
|
||||
return Err(AdvancedCommonWebError::NotAuthorized);
|
||||
return Err(CommonWebError::NotAuthorized);
|
||||
}
|
||||
|
||||
// 2. Look up the target user by username.
|
||||
@@ -83,10 +83,10 @@ pub async fn moderation_ban_user_handler(
|
||||
&server_state.mysql_pool,
|
||||
).await.map_err(|err| {
|
||||
warn!("User lookup error: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
CommonWebError::from_anyhow_error(err)
|
||||
})?.ok_or_else(|| {
|
||||
warn!("User not found for ban: {}", username_lower);
|
||||
AdvancedCommonWebError::NotFound
|
||||
CommonWebError::NotFound
|
||||
})?;
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
@@ -109,7 +109,7 @@ pub async fn moderation_ban_user_handler(
|
||||
let mut transaction = server_state.mysql_pool.begin().await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to begin transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// 4. Set ban status.
|
||||
@@ -122,7 +122,7 @@ pub async fn moderation_ban_user_handler(
|
||||
phantom: PhantomData,
|
||||
}).await.map_err(|err| {
|
||||
warn!("Failed to set user ban status: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// 5. Insert staff audit log.
|
||||
@@ -136,13 +136,13 @@ pub async fn moderation_ban_user_handler(
|
||||
phantom: PhantomData,
|
||||
}).await.map_err(|err| {
|
||||
warn!("Failed to insert staff audit log: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
// 6. Commit transaction.
|
||||
transaction.commit().await.map_err(|err| {
|
||||
warn!("Failed to commit transaction: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user