diff --git a/.sqlx/query-7f874f4ac43d8c66e3ea8cb2fb56f57b93047355dd9faee4df48799f6d1be00a.json b/.sqlx/query-7f874f4ac43d8c66e3ea8cb2fb56f57b93047355dd9faee4df48799f6d1be00a.json new file mode 100644 index 0000000000..e627babe38 --- /dev/null +++ b/.sqlx/query-7f874f4ac43d8c66e3ea8cb2fb56f57b93047355dd9faee4df48799f6d1be00a.json @@ -0,0 +1,146 @@ +{ + "db_name": "MySQL", + "query": "\nSELECT\n token as `token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,\n wallet_token as `wallet_token: tokens::tokens::wallets::WalletToken`,\n entry_type as `entry_type: enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType`,\n maybe_entity_ref,\n credits_delta,\n banked_credits_before as `banked_credits_before: u32`,\n banked_credits_after as `banked_credits_after: u32`,\n monthly_credits_before as `monthly_credits_before: u32`,\n monthly_credits_after as `monthly_credits_after: u32`,\n created_at as `created_at: DateTime`,\n is_refunded as `is_refunded: bool`,\n maybe_linked_refund_ledger_token as `maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`\nFROM wallet_ledger_entries\nWHERE wallet_token = ?\nORDER BY id DESC\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE", + "char_set": 224, + "max_size": 128 + } + }, + { + "ordinal": 1, + "name": "wallet_token: tokens::tokens::wallets::WalletToken", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE", + "char_set": 224, + "max_size": 128 + } + }, + { + "ordinal": 2, + "name": "entry_type: enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE", + "char_set": 224, + "max_size": 128 + } + }, + { + "ordinal": 3, + "name": "maybe_entity_ref", + "type_info": { + "type": "VarString", + "flags": "BINARY", + "char_set": 224, + "max_size": 1020 + } + }, + { + "ordinal": 4, + "name": "credits_delta", + "type_info": { + "type": "Long", + "flags": "NOT_NULL", + "char_set": 63, + "max_size": 11 + } + }, + { + "ordinal": 5, + "name": "banked_credits_before: u32", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | UNSIGNED", + "char_set": 63, + "max_size": 10 + } + }, + { + "ordinal": 6, + "name": "banked_credits_after: u32", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | UNSIGNED", + "char_set": 63, + "max_size": 10 + } + }, + { + "ordinal": 7, + "name": "monthly_credits_before: u32", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | UNSIGNED", + "char_set": 63, + "max_size": 10 + } + }, + { + "ordinal": 8, + "name": "monthly_credits_after: u32", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | UNSIGNED", + "char_set": 63, + "max_size": 10 + } + }, + { + "ordinal": 9, + "name": "created_at: DateTime", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "char_set": 63, + "max_size": 19 + } + }, + { + "ordinal": 10, + "name": "is_refunded: bool", + "type_info": { + "type": "Tiny", + "flags": "NOT_NULL", + "char_set": 63, + "max_size": 1 + } + }, + { + "ordinal": 11, + "name": "maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken", + "type_info": { + "type": "VarString", + "flags": "BINARY", + "char_set": 224, + "max_size": 128 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true + ] + }, + "hash": "7f874f4ac43d8c66e3ea8cb2fb56f57b93047355dd9faee4df48799f6d1be00a" +} diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/mod.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/mod.rs index e6a0449966..43f96bb232 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/mod.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/mod.rs @@ -1,3 +1,4 @@ pub mod jobs; pub mod user; +pub mod wallet_ledger_entries; pub mod wallets; diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet.rs new file mode 100644 index 0000000000..2b0d33612e --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet.rs @@ -0,0 +1,35 @@ +use chrono::{DateTime, Utc}; +use enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType; +use serde_derive::{Deserialize, Serialize}; +use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken; +use tokens::tokens::wallets::WalletToken; +use utoipa::ToSchema; + +pub const LIST_WALLET_LEDGER_ENTRIES_BY_WALLET_PATH: &str = "/v1/moderation/wallet_ledger_entries/wallet/{wallet_token}/list"; + +#[derive(Deserialize, ToSchema)] +pub struct ListWalletLedgerEntriesByWalletPathInfo { + pub wallet_token: WalletToken, +} + +#[derive(Serialize, ToSchema)] +pub struct ListWalletLedgerEntriesByWalletResponse { + pub success: bool, + pub entries: Vec, +} + +#[derive(Serialize, ToSchema)] +pub struct ListWalletLedgerEntriesByWalletEntry { + pub token: WalletLedgerEntryToken, + pub wallet_token: WalletToken, + pub entry_type: WalletLedgerEntryType, + pub maybe_entity_ref: Option, + pub credits_delta: i32, + pub banked_credits_before: u32, + pub banked_credits_after: u32, + pub monthly_credits_before: u32, + pub monthly_credits_after: u32, + pub created_at: DateTime, + pub is_refunded: bool, + pub maybe_linked_refund_ledger_token: Option, +} diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/wallet_ledger_entries/mod.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/wallet_ledger_entries/mod.rs new file mode 100644 index 0000000000..fe94f8a343 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/moderation/wallet_ledger_entries/mod.rs @@ -0,0 +1 @@ +pub mod list_wallet_ledger_entries_by_wallet; diff --git a/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet.rs b/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet.rs new file mode 100644 index 0000000000..36d3f1a635 --- /dev/null +++ b/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet.rs @@ -0,0 +1,62 @@ +use anyhow::anyhow; +use log::warn; +use sqlx::MySqlPool; + +use chrono::{DateTime, Utc}; +use enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType; +use errors::AnyhowResult; +use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken; +use tokens::tokens::wallets::WalletToken; + +pub struct WalletLedgerEntryForModerationResult { + pub token: WalletLedgerEntryToken, + pub wallet_token: WalletToken, + pub entry_type: WalletLedgerEntryType, + pub maybe_entity_ref: Option, + pub credits_delta: i32, + pub banked_credits_before: u32, + pub banked_credits_after: u32, + pub monthly_credits_before: u32, + pub monthly_credits_after: u32, + pub created_at: DateTime, + pub is_refunded: bool, + pub maybe_linked_refund_ledger_token: Option, +} + +pub async fn list_wallet_ledger_entries_by_wallet( + wallet_token: &WalletToken, + mysql_pool: &MySqlPool, +) -> AnyhowResult> { + let results = sqlx::query_as!( + WalletLedgerEntryForModerationResult, + r#" +SELECT + token as `token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`, + wallet_token as `wallet_token: tokens::tokens::wallets::WalletToken`, + entry_type as `entry_type: enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType`, + maybe_entity_ref, + credits_delta, + banked_credits_before as `banked_credits_before: u32`, + banked_credits_after as `banked_credits_after: u32`, + monthly_credits_before as `monthly_credits_before: u32`, + monthly_credits_after as `monthly_credits_after: u32`, + created_at as `created_at: DateTime`, + is_refunded as `is_refunded: bool`, + maybe_linked_refund_ledger_token as `maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken` +FROM wallet_ledger_entries +WHERE wallet_token = ? +ORDER BY id DESC + "#, + wallet_token, + ) + .fetch_all(mysql_pool) + .await; + + match results { + Ok(records) => Ok(records), + Err(err) => { + warn!("list_wallet_ledger_entries_by_wallet query error: {:?}", err); + Err(anyhow!("query error")) + } + } +} diff --git a/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/mod.rs b/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/mod.rs index 46661ede68..148a6cda7a 100644 --- a/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/mod.rs +++ b/crates/schema/database/mysql_queries/src/queries/wallet_ledger_entries/mod.rs @@ -1,2 +1,3 @@ pub (crate) mod internal_insert_wallet_ledger_entry; -pub mod internal_insert_wallet_created_ledger_entry; \ No newline at end of file +pub mod internal_insert_wallet_created_ledger_entry; +pub mod list_wallet_ledger_entries_by_wallet; \ No newline at end of file diff --git a/crates/service/web/storyteller_web/src/docs/api_doc.rs b/crates/service/web/storyteller_web/src/docs/api_doc.rs index fa81242660..dca63fccce 100644 --- a/crates/service/web/storyteller_web/src/docs/api_doc.rs +++ b/crates/service/web/storyteller_web/src/docs/api_doc.rs @@ -268,6 +268,7 @@ use crate::http_server::endpoints::moderation::info::moderator_token_info_handle use artcraft_api_defs::moderation::user::user_lookup::*; use artcraft_api_defs::moderation::user::user_lookup_by_stripe_customer_id::*; use artcraft_api_defs::moderation::jobs::user::list_user_jobs::*; +use artcraft_api_defs::moderation::wallet_ledger_entries::list_wallet_ledger_entries_by_wallet::*; use artcraft_api_defs::moderation::wallets::list_user_wallets::*; use crate::http_server::endpoints::webhooks::fal_webhook_handler::*; use crate::http_server::endpoints::image_studio::update_gpt_image_job_status_handler::*; @@ -441,6 +442,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux crate::http_server::endpoints::moderation::user::moderator_user_lookup_handler::moderator_user_lookup_handler, crate::http_server::endpoints::moderation::user::moderator_user_lookup_by_stripe_customer_id_handler::moderator_user_lookup_by_stripe_customer_id_handler, crate::http_server::endpoints::moderation::jobs::user::list_user_jobs_handler::list_user_jobs_handler, + crate::http_server::endpoints::moderation::wallet_ledger_entries::list_wallet_ledger_entries_by_wallet_handler::list_wallet_ledger_entries_by_wallet_handler, crate::http_server::endpoints::moderation::wallets::list_user_wallets_handler::list_user_wallets_handler, // Credits crate::http_server::endpoints::credits::get_session_credits_handler::get_session_credits_handler, @@ -631,6 +633,9 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux ListUserWalletsPathInfo, ListUserWalletsResponse, ListUserWalletsEntry, + ListWalletLedgerEntriesByWalletPathInfo, + ListWalletLedgerEntriesByWalletResponse, + ListWalletLedgerEntriesByWalletEntry, GptImage1EditImageRequest, GptImage1EditImageImageSize, GptImage1EditImageNumImages, diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/mod.rs index 654ad4e32c..312833231b 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/mod.rs @@ -4,4 +4,5 @@ pub mod jobs; pub mod user; pub mod user_bans; pub mod user_feature_flags; +pub mod wallet_ledger_entries; pub mod wallets; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet_handler.rs new file mode 100644 index 0000000000..9b6af26d58 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/wallet_ledger_entries/list_wallet_ledger_entries_by_wallet_handler.rs @@ -0,0 +1,68 @@ +use std::sync::Arc; + +use actix_web::web::{Json, Path}; +use actix_web::{web, HttpRequest}; +use log::warn; + +use artcraft_api_defs::moderation::wallet_ledger_entries::list_wallet_ledger_entries_by_wallet::{ + ListWalletLedgerEntriesByWalletEntry, + ListWalletLedgerEntriesByWalletPathInfo, + ListWalletLedgerEntriesByWalletResponse, +}; +use mysql_queries::queries::wallet_ledger_entries::list_wallet_ledger_entries_by_wallet::list_wallet_ledger_entries_by_wallet; + +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; + +/// List wallet ledger entries by wallet token (moderation) +#[utoipa::path( + get, + tag = "Moderation", + path = "/v1/moderation/wallet_ledger_entries/wallet/{wallet_token}/list", + responses( + (status = 200, description = "Success", body = ListWalletLedgerEntriesByWalletResponse), + (status = 401, description = "Unauthorized"), + (status = 500, description = "Server error"), + ), + params( + ("wallet_token" = WalletToken, Path, description = "Wallet token to look up ledger entries for"), + ) +)] +pub async fn list_wallet_ledger_entries_by_wallet_handler( + http_request: HttpRequest, + path: Path, + server_state: web::Data>, +) -> Result, CommonWebError> { + + let _user_session = require_moderator(&http_request, &server_state, UseDatabase::Implicit) + .await + .map_err(|_| CommonWebError::NotAuthorized)?; + + let results = list_wallet_ledger_entries_by_wallet(&path.wallet_token, &server_state.mysql_pool) + .await + .map_err(|err| { + warn!("list_wallet_ledger_entries_by_wallet error: {:?}", err); + CommonWebError::ServerError + })?; + + let entries = results.into_iter().map(|row| ListWalletLedgerEntriesByWalletEntry { + token: row.token, + wallet_token: row.wallet_token, + entry_type: row.entry_type, + maybe_entity_ref: row.maybe_entity_ref, + credits_delta: row.credits_delta, + banked_credits_before: row.banked_credits_before, + banked_credits_after: row.banked_credits_after, + monthly_credits_before: row.monthly_credits_before, + monthly_credits_after: row.monthly_credits_after, + created_at: row.created_at, + is_refunded: row.is_refunded, + maybe_linked_refund_ledger_token: row.maybe_linked_refund_ledger_token, + }).collect(); + + Ok(Json(ListWalletLedgerEntriesByWalletResponse { + success: true, + entries, + })) +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/wallet_ledger_entries/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/wallet_ledger_entries/mod.rs new file mode 100644 index 0000000000..ca2646996f --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/moderation/wallet_ledger_entries/mod.rs @@ -0,0 +1 @@ +pub mod list_wallet_ledger_entries_by_wallet_handler; diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/moderation_routes.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/moderation_routes.rs index d4f05580f5..ee7e135996 100644 --- a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/moderation_routes.rs +++ b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/moderation_routes.rs @@ -20,6 +20,7 @@ use crate::http_server::endpoints::moderation::ip_bans::delete_ip_ban::delete_ip use crate::http_server::endpoints::moderation::ip_bans::get_ip_ban::get_ip_ban_handler; use crate::http_server::endpoints::moderation::ip_bans::list_ip_bans::list_ip_bans_handler; use crate::http_server::endpoints::moderation::jobs::user::list_user_jobs_handler::list_user_jobs_handler; +use crate::http_server::endpoints::moderation::wallet_ledger_entries::list_wallet_ledger_entries_by_wallet_handler::list_wallet_ledger_entries_by_wallet_handler; use crate::http_server::endpoints::moderation::wallets::list_user_wallets_handler::list_user_wallets_handler; use crate::http_server::endpoints::moderation::jobs::get_tts_inference_queue_count::get_tts_inference_queue_count_handler; use crate::http_server::endpoints::moderation::jobs::get_w2l_inference_queue_count::get_w2l_inference_queue_count_handler; @@ -68,6 +69,12 @@ pub fn add_moderator_routes (app: App) -> App .route(web::head().to(|| HttpResponse::Ok())) ) ) + .service(web::scope("/wallet_ledger_entries") + .service(web::resource("/wallet/{wallet_token}/list") + .route(web::get().to(list_wallet_ledger_entries_by_wallet_handler)) + .route(web::head().to(|| HttpResponse::Ok())) + ) + ) ) // NB: Old paths are not under /v1 - .service(web::scope("/moderation")