diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/lib.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/lib.rs index a161c4065f..3828a3aa94 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/lib.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/lib.rs @@ -10,6 +10,7 @@ pub mod omni_gen; pub mod prompts; pub mod stripe_artcraft; pub mod subscriptions; +pub mod user_referral_codes; pub mod users; pub mod utils; pub mod web_referrals; \ No newline at end of file diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/create_referral_code.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/create_referral_code.rs new file mode 100644 index 0000000000..d41ed315b7 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/create_referral_code.rs @@ -0,0 +1,17 @@ +use serde_derive::{Deserialize, Serialize}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use utoipa::ToSchema; + +#[derive(Deserialize, ToSchema)] +pub struct CreateReferralCodeRequest { + /// The referral code string. Alphanumeric plus underscore, period, dash. + pub code: String, +} + +#[derive(Serialize, ToSchema)] +pub struct CreateReferralCodeResponse { + pub success: bool, + pub token: UserReferralCodeToken, + pub code: String, + pub code_lowercase: String, +} diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/delete_referral_code.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/delete_referral_code.rs new file mode 100644 index 0000000000..cebe7009cb --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/delete_referral_code.rs @@ -0,0 +1,13 @@ +use serde_derive::{Deserialize, Serialize}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use utoipa::ToSchema; + +#[derive(Deserialize, ToSchema)] +pub struct DeleteReferralCodePathInfo { + pub token: UserReferralCodeToken, +} + +#[derive(Serialize, ToSchema)] +pub struct DeleteReferralCodeResponse { + pub success: bool, +} diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/list_referral_codes.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/list_referral_codes.rs new file mode 100644 index 0000000000..fb1321ad68 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/list_referral_codes.rs @@ -0,0 +1,19 @@ +use chrono::{DateTime, Utc}; +use serde_derive::{Deserialize, Serialize}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use utoipa::ToSchema; + +#[derive(Serialize, ToSchema)] +pub struct ListReferralCodesResponse { + pub success: bool, + pub referral_codes: Vec, +} + +#[derive(Serialize, ToSchema)] +pub struct ReferralCodeEntry { + pub token: UserReferralCodeToken, + pub code: String, + pub code_lowercase: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/mod.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/mod.rs new file mode 100644 index 0000000000..97274226d9 --- /dev/null +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/user_referral_codes/mod.rs @@ -0,0 +1,3 @@ +pub mod create_referral_code; +pub mod delete_referral_code; +pub mod list_referral_codes; diff --git a/crates/schema/database/mysql_queries/src/queries/mod.rs b/crates/schema/database/mysql_queries/src/queries/mod.rs index b48a436b44..162dafe5a6 100644 --- a/crates/schema/database/mysql_queries/src/queries/mod.rs +++ b/crates/schema/database/mysql_queries/src/queries/mod.rs @@ -36,6 +36,7 @@ pub mod tts; pub mod twitch; pub mod unsubscribe_reason; pub mod user_impersonation_requests; +pub mod user_referral_codes; pub mod users; pub mod vocoder; pub mod voice_clone_requests; diff --git a/crates/schema/database/mysql_queries/src/queries/user_referral_codes/create_referral_code.rs b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/create_referral_code.rs new file mode 100644 index 0000000000..c53334b7b7 --- /dev/null +++ b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/create_referral_code.rs @@ -0,0 +1,39 @@ +use sqlx::{Executor, MySql}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use tokens::tokens::users::UserToken; + +pub struct CreateReferralCodeArgs<'a> { + pub owner_user_token: &'a UserToken, + pub code: &'a str, + pub code_lowercase: &'a str, +} + +pub async fn create_referral_code<'e, 'c: 'e, E>( + args: CreateReferralCodeArgs<'_>, + mysql_executor: E, +) -> Result +where + E: 'e + Executor<'c, Database = MySql>, +{ + let token = UserReferralCodeToken::generate(); + + sqlx::query!( + r#" +INSERT INTO user_referral_codes ( + token, + code, + code_lowercase, + owner_user_token +) +VALUES (?, ?, ?, ?) + "#, + token.as_str(), + args.code, + args.code_lowercase, + args.owner_user_token.as_str(), + ) + .execute(mysql_executor) + .await?; + + Ok(token) +} diff --git a/crates/schema/database/mysql_queries/src/queries/user_referral_codes/list_referral_codes_for_user.rs b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/list_referral_codes_for_user.rs new file mode 100644 index 0000000000..e4af63b789 --- /dev/null +++ b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/list_referral_codes_for_user.rs @@ -0,0 +1,55 @@ +use chrono::{DateTime, NaiveDateTime, Utc}; +use sqlx::{Executor, MySql}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use tokens::tokens::users::UserToken; + +pub struct ReferralCodeRow { + pub token: UserReferralCodeToken, + pub code: String, + pub code_lowercase: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +struct RawRow { + token: UserReferralCodeToken, + code: String, + code_lowercase: String, + created_at: NaiveDateTime, + updated_at: NaiveDateTime, +} + +pub async fn list_referral_codes_for_user<'e, 'c: 'e, E>( + owner_user_token: &UserToken, + mysql_executor: E, +) -> Result, sqlx::Error> +where + E: 'e + Executor<'c, Database = MySql>, +{ + let rows = sqlx::query_as!( + RawRow, + r#" +SELECT + token as `token: UserReferralCodeToken`, + code, + code_lowercase, + created_at, + updated_at +FROM user_referral_codes +WHERE owner_user_token = ? + AND deleted_at IS NULL +ORDER BY created_at ASC + "#, + owner_user_token.as_str(), + ) + .fetch_all(mysql_executor) + .await?; + + Ok(rows.into_iter().map(|r| ReferralCodeRow { + token: r.token, + code: r.code, + code_lowercase: r.code_lowercase, + created_at: r.created_at.and_utc(), + updated_at: r.updated_at.and_utc(), + }).collect()) +} diff --git a/crates/schema/database/mysql_queries/src/queries/user_referral_codes/mod.rs b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/mod.rs new file mode 100644 index 0000000000..434852bea3 --- /dev/null +++ b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/mod.rs @@ -0,0 +1,3 @@ +pub mod create_referral_code; +pub mod list_referral_codes_for_user; +pub mod soft_delete_referral_code; diff --git a/crates/schema/database/mysql_queries/src/queries/user_referral_codes/soft_delete_referral_code.rs b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/soft_delete_referral_code.rs new file mode 100644 index 0000000000..bd300d4e7f --- /dev/null +++ b/crates/schema/database/mysql_queries/src/queries/user_referral_codes/soft_delete_referral_code.rs @@ -0,0 +1,39 @@ +use sqlx::{Executor, MySql}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use tokens::tokens::users::UserToken; +use uuid_utils::uuid::generate_random_uuid; + +/// Soft-delete a referral code owned by the given user. +/// Sets `deleted_at` and replaces `code_lowercase` with random entropy +/// to free up the unique constraint for reuse. +/// Returns true if a row was updated. +pub async fn soft_delete_referral_code<'e, 'c: 'e, E>( + token: &UserReferralCodeToken, + owner_user_token: &UserToken, + mysql_executor: E, +) -> Result +where + E: 'e + Executor<'c, Database = MySql>, +{ + // Replace code_lowercase with random entropy (max 32 chars) to free uniqueness constraint. + let tombstone = format!("_deleted_{}", &generate_random_uuid()[..20]); + + let result = sqlx::query!( + r#" +UPDATE user_referral_codes +SET + deleted_at = CURRENT_TIMESTAMP, + code_lowercase = ? +WHERE token = ? + AND owner_user_token = ? + AND deleted_at IS NULL + "#, + tombstone, + token.as_str(), + owner_user_token.as_str(), + ) + .execute(mysql_executor) + .await?; + + Ok(result.rows_affected() > 0) +} 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 41a4880537..e4e3c3f0fc 100644 --- a/crates/service/web/storyteller_web/src/docs/api_doc.rs +++ b/crates/service/web/storyteller_web/src/docs/api_doc.rs @@ -316,6 +316,9 @@ use artcraft_api_defs::moderation::wallets::list_user_wallets::*; use artcraft_api_defs::moderation::wallets::moderator_add_banked_balance_to_wallet::*; use artcraft_api_defs::moderation::wallets::moderator_create_wallet_for_user::*; use artcraft_api_defs::moderation::wallets::moderator_get_wallet::*; +use artcraft_api_defs::user_referral_codes::create_referral_code::*; +use artcraft_api_defs::user_referral_codes::delete_referral_code::*; +use artcraft_api_defs::user_referral_codes::list_referral_codes::*; use artcraft_api_defs::web_referrals::log_web_referral::*; use crate::http_server::endpoints::web_referrals::log_web_referral_handler::*; use crate::http_server::endpoints::image_studio::update_gpt_image_job_status_handler::*; @@ -536,6 +539,10 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux crate::http_server::endpoints::subscriptions::get_session_subscription_handler::get_session_subscription_handler, // Web Referrals crate::http_server::endpoints::web_referrals::log_web_referral_handler::log_web_referral_handler, + // User Referral Codes + crate::http_server::endpoints::user_referral_codes::create_referral_code_handler::create_referral_code_handler, + crate::http_server::endpoints::user_referral_codes::list_referral_codes_handler::list_referral_codes_handler, + crate::http_server::endpoints::user_referral_codes::delete_referral_code_handler::delete_referral_code_handler, // Image Studio crate::http_server::endpoints::image_studio::update_gpt_image_job_status_handler::update_gpt_image_job_status_handler, ), @@ -1210,6 +1217,14 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux // Web Referrals LogWebReferralRequest, LogWebReferralResponse, + + // User Referral Codes + CreateReferralCodeRequest, + CreateReferralCodeResponse, + ListReferralCodesResponse, + ReferralCodeEntry, + DeleteReferralCodePathInfo, + DeleteReferralCodeResponse, )) )] pub struct ApiDoc; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs index cfc5476cb7..f725950848 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs @@ -25,6 +25,7 @@ pub mod trending; pub mod tts; pub mod user_bookmarks; pub mod user_ratings; +pub mod user_referral_codes; pub mod user_wallets; pub mod users; pub mod voice_clone_requests; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/create_referral_code_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/create_referral_code_handler.rs new file mode 100644 index 0000000000..56539ea561 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/create_referral_code_handler.rs @@ -0,0 +1,110 @@ +use std::sync::Arc; + +use actix_web::web::{self, Json}; +use actix_web::HttpRequest; +use log::warn; +use utoipa::ToSchema; + +use artcraft_api_defs::user_referral_codes::create_referral_code::{CreateReferralCodeRequest, CreateReferralCodeResponse}; +use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError; +use crate::state::server_state::ServerState; +use mysql_queries::queries::user_referral_codes::create_referral_code::{create_referral_code, CreateReferralCodeArgs}; +use mysql_queries::queries::user_referral_codes::list_referral_codes_for_user::list_referral_codes_for_user; + +const MAX_ACTIVE_CODES: usize = 5; + +/// Create a new referral code for the logged-in user. +#[utoipa::path( + post, + tag = "User Referral Codes", + path = "/v1/user_referral_codes/create", + request_body = CreateReferralCodeRequest, + responses( + (status = 200, description = "Success", body = CreateReferralCodeResponse), + (status = 400, description = "Bad input"), + (status = 401, description = "Unauthorized"), + (status = 500, description = "Server error"), + ), +)] +pub async fn create_referral_code_handler( + http_request: HttpRequest, + server_state: web::Data>, + request: web::Json, +) -> Result, AdvancedCommonWebError> { + let mut mysql_connection = server_state.mysql_pool.acquire().await + .map_err(|e| AdvancedCommonWebError::from(e))?; + + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session_from_connection(&http_request, &mut mysql_connection) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + AdvancedCommonWebError::from(e) + })?; + + let user_session = match maybe_user_session { + Some(session) if !session.is_banned => session, + _ => return Err(AdvancedCommonWebError::NotAuthorized), + }; + + let user_token = &user_session.user_token; + + // Validate code: trim, check length, check characters. + let code = request.code.trim().to_string(); + + if code.is_empty() { + return Err(AdvancedCommonWebError::BadInputWithSimpleMessage( + "Referral code cannot be empty".to_string(), + )); + } + + if code.len() > 32 { + return Err(AdvancedCommonWebError::BadInputWithSimpleMessage( + "Referral code must be 32 characters or fewer".to_string(), + )); + } + + if !code.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '.' || c == '-') { + return Err(AdvancedCommonWebError::BadInputWithSimpleMessage( + "Referral code may only contain letters, numbers, underscores, periods, and dashes".to_string(), + )); + } + + let code_lowercase = code.to_lowercase(); + + // Check the user doesn't already have too many active codes. + let existing = list_referral_codes_for_user(user_token, &mut *mysql_connection).await + .map_err(|e| AdvancedCommonWebError::from(e))?; + + if existing.len() >= MAX_ACTIVE_CODES { + return Err(AdvancedCommonWebError::BadInputWithSimpleMessage( + format!("You can have at most {} active referral codes. Delete one first.", MAX_ACTIVE_CODES), + )); + } + + // Insert the code. If code_lowercase is already taken, we get a duplicate key error. + let token = create_referral_code( + CreateReferralCodeArgs { + owner_user_token: user_token, + code: &code, + code_lowercase: &code_lowercase, + }, + &mut *mysql_connection, + ).await; + + match token { + Ok(token) => Ok(Json(CreateReferralCodeResponse { + success: true, + token, + code, + code_lowercase, + })), + Err(sqlx::Error::Database(db_err)) if db_err.code().as_deref() == Some("23000") => { + Err(AdvancedCommonWebError::BadInputWithSimpleMessage( + "This referral code is already in use".to_string(), + )) + } + Err(e) => Err(AdvancedCommonWebError::from(e)), + } +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/delete_referral_code_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/delete_referral_code_handler.rs new file mode 100644 index 0000000000..541d8a91b7 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/delete_referral_code_handler.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; + +use actix_web::web::{self, Json}; +use actix_web::HttpRequest; +use log::warn; + +use artcraft_api_defs::user_referral_codes::delete_referral_code::{DeleteReferralCodePathInfo, DeleteReferralCodeResponse}; +use tokens::tokens::user_referral_codes::UserReferralCodeToken; +use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError; +use crate::state::server_state::ServerState; +use mysql_queries::queries::user_referral_codes::soft_delete_referral_code::soft_delete_referral_code; + +/// Delete (soft-delete) a referral code owned by the logged-in user. +#[utoipa::path( + delete, + tag = "User Referral Codes", + path = "/v1/user_referral_codes/code/{token}", + params( + ("token" = UserReferralCodeToken, description = "The referral code token to delete"), + ), + responses( + (status = 200, description = "Success", body = DeleteReferralCodeResponse), + (status = 401, description = "Unauthorized"), + (status = 500, description = "Server error"), + ), +)] +pub async fn delete_referral_code_handler( + http_request: HttpRequest, + server_state: web::Data>, + path: web::Path, +) -> Result, AdvancedCommonWebError> { + let mut mysql_connection = server_state.mysql_pool.acquire().await + .map_err(|e| AdvancedCommonWebError::from(e))?; + + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session_from_connection(&http_request, &mut mysql_connection) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + AdvancedCommonWebError::from(e) + })?; + + let user_session = match maybe_user_session { + Some(session) if !session.is_banned => session, + _ => return Err(AdvancedCommonWebError::NotAuthorized), + }; + + let user_token = &user_session.user_token; + + let deleted = soft_delete_referral_code( + &path.token, + user_token, + &mut *mysql_connection, + ).await + .map_err(|e| AdvancedCommonWebError::from(e))?; + + if !deleted { + return Err(AdvancedCommonWebError::NotAuthorized); + } + + Ok(Json(DeleteReferralCodeResponse { + success: true, + })) +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/list_referral_codes_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/list_referral_codes_handler.rs new file mode 100644 index 0000000000..d5ebb1bdc2 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/list_referral_codes_handler.rs @@ -0,0 +1,61 @@ +use std::sync::Arc; + +use actix_web::web::{self, Json}; +use actix_web::HttpRequest; +use log::warn; + +use artcraft_api_defs::user_referral_codes::list_referral_codes::{ListReferralCodesResponse, ReferralCodeEntry}; +use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError; +use crate::state::server_state::ServerState; +use mysql_queries::queries::user_referral_codes::list_referral_codes_for_user::list_referral_codes_for_user; + +/// List all active referral codes for the logged-in user. +#[utoipa::path( + get, + tag = "User Referral Codes", + path = "/v1/user_referral_codes/list", + responses( + (status = 200, description = "Success", body = ListReferralCodesResponse), + (status = 401, description = "Unauthorized"), + (status = 500, description = "Server error"), + ), +)] +pub async fn list_referral_codes_handler( + http_request: HttpRequest, + server_state: web::Data>, +) -> Result, AdvancedCommonWebError> { + let mut mysql_connection = server_state.mysql_pool.acquire().await + .map_err(|e| AdvancedCommonWebError::from(e))?; + + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session_from_connection(&http_request, &mut mysql_connection) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + AdvancedCommonWebError::from(e) + })?; + + let user_session = match maybe_user_session { + Some(session) if !session.is_banned => session, + _ => return Err(AdvancedCommonWebError::NotAuthorized), + }; + + let user_token = &user_session.user_token; + + let rows = list_referral_codes_for_user(user_token, &mut *mysql_connection).await + .map_err(|e| AdvancedCommonWebError::from(e))?; + + let referral_codes = rows.into_iter().map(|r| ReferralCodeEntry { + token: r.token, + code: r.code, + code_lowercase: r.code_lowercase, + created_at: r.created_at, + updated_at: r.updated_at, + }).collect(); + + Ok(Json(ListReferralCodesResponse { + success: true, + referral_codes, + })) +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/mod.rs new file mode 100644 index 0000000000..0a95d19a30 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_referral_codes/mod.rs @@ -0,0 +1,3 @@ +pub mod create_referral_code_handler; +pub mod delete_referral_code_handler; +pub mod list_referral_codes_handler; diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs index f243cb42c2..cf3dc7c615 100644 --- a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs +++ b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs @@ -15,6 +15,7 @@ use crate::http_server::routes::application_routes::subscription_routes::add_sub use crate::http_server::routes::application_routes::tag_routes::add_tag_routes; use crate::http_server::routes::application_routes::user_bookmarks_routes::add_user_bookmarks_routes; use crate::http_server::routes::application_routes::user_rating_routes::add_user_rating_routes; +use crate::http_server::routes::application_routes::user_referral_code_routes::add_user_referral_code_routes; use crate::http_server::routes::application_routes::user_routes::add_user_routes; use crate::http_server::routes::application_routes::wallet_routes::add_wallet_routes; use crate::http_server::routes::application_routes::web_referrals_routes::add_web_referrals_routes; @@ -67,6 +68,7 @@ where app = add_comments_routes(app); // /v1/comments/... app = add_user_bookmarks_routes(app); // /v1/user_bookmarks/... app = add_user_rating_routes(app); // /v1/user_rating/... + app = add_user_referral_code_routes(app); // /v1/user_referral_codes/... app = add_user_routes(app); // /create_account, /session, /login, /logout, etc. // Artcraft Billing pieces diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs index 120b3fe977..dff9844eae 100644 --- a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs @@ -15,6 +15,7 @@ mod subscription_routes; mod tag_routes; mod user_bookmarks_routes; mod user_rating_routes; +mod user_referral_code_routes; mod user_routes; mod wallet_routes; mod web_referrals_routes; diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/user_referral_code_routes.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/user_referral_code_routes.rs new file mode 100644 index 0000000000..0507d56654 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/user_referral_code_routes.rs @@ -0,0 +1,28 @@ +use actix_service::ServiceFactory; +use actix_web::body::MessageBody; +use actix_web::dev::{ServiceRequest, ServiceResponse}; +use actix_web::{web, App, Error, HttpResponse}; + +use crate::http_server::endpoints::user_referral_codes::create_referral_code_handler::create_referral_code_handler; +use crate::http_server::endpoints::user_referral_codes::delete_referral_code_handler::delete_referral_code_handler; +use crate::http_server::endpoints::user_referral_codes::list_referral_codes_handler::list_referral_codes_handler; + +pub fn add_user_referral_code_routes(app: App) -> App +where + T: ServiceFactory, InitError = ()>, + B: MessageBody, +{ + app + .service(web::resource("/v1/user_referral_codes/create") + .route(web::post().to(create_referral_code_handler)) + .route(web::head().to(|| HttpResponse::Ok())) + ) + .service(web::resource("/v1/user_referral_codes/list") + .route(web::get().to(list_referral_codes_handler)) + .route(web::head().to(|| HttpResponse::Ok())) + ) + .service(web::resource("/v1/user_referral_codes/code/{token}") + .route(web::delete().to(delete_referral_code_handler)) + .route(web::head().to(|| HttpResponse::Ok())) + ) +}