This commit is contained in:
Brandon Thomas committed 2026-01-30 20:39:03 -05:00
1 parent 8e361a2d8b
commit 55f557278a
14 files changed
+210 -85

No files matched your search

Generated
+15
View File
@@ -330,6 +330,19 @@ dependencies = [
"syn 2.0.106",
]
[[package]]
name = "actix_artcraft"
version = "0.0.1"
dependencies = [
"actix-http",
"actix-service",
"actix-web",
"actix_helpers",
"enums",
"log",
"workspace-build-acceleration",
]
[[package]]
name = "actix_cors_configs"
version = "0.0.1"
@@ -1351,6 +1364,7 @@ version = "0.0.1"
dependencies = [
"actix-http",
"actix-web",
"actix_artcraft",
"anyhow",
"artcraft_api_defs",
"async-stripe 1.0.0-alpha.2",
@@ -10719,6 +10733,7 @@ dependencies = [
"actix-web",
"actix-web-actors",
"actix-web-lab",
"actix_artcraft",
"actix_cors_configs",
"actix_helpers",
"anyhow",
+3
View File
@@ -36,6 +36,7 @@ members = [
"crates/desktop/artcraft",
# Shared libraries
"crates/lib/actix_artcraft",
"crates/lib/actix_cors_configs",
"crates/lib/actix_helpers",
"crates/lib/bootstrap",
@@ -186,6 +187,8 @@ workspace-build-acceleration = { path = "crates/workspace-build-acceleration" }
# Internal libs
# Setting paths here ensures we don't need to do so in every crate's Cargo.toml
# It makes refactoring much easier.
actix_artcraft = { path = "crates/lib/actix_artcraft" }
actix_helpers = { path = "crates/lib/actix_helpers" }
artcraft_api_defs = { path = "crates/api_clients/artcraft/artcraft_api_defs" }
build_metadata = { path = "crates/lib/build_metadata" }
cloud_storage = { path = "crates/api_clients/cloud_storage" }
+34
View File
@@ -0,0 +1,34 @@
# For things that are too application-logic specific to live in generic actix helpers.
[package]
name = "actix_artcraft"
edition = "2024"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[package.metadata.cargo-udeps.ignore]
normal = ["workspace-build-acceleration"]
[lib]
name = "actix_artcraft"
path = "src/lib.rs"
[dependencies]
# Workspace hack (faster compile times)
workspace-build-acceleration.workspace = true
# Internal
actix_helpers.workspace = true
enums.workspace = true
# External
actix-http.workspace = true
actix-service.workspace = true
actix-web.workspace = true
log.workspace = true
[dev-dependencies]
# (None yet)
+20
View File
@@ -0,0 +1,20 @@
//! actix_artcraft - for actix helpers that need to be shared between crates,
//! but are too application-specific to be in general actix helper crates
//!
// Never allow these
#![forbid(private_bounds)]
#![forbid(private_interfaces)]
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
// Okay to toggle
#![forbid(unreachable_patterns)]
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
// Always allow
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod requests;
@@ -1,6 +1,5 @@
use actix_helpers::extractors::get_request_origin_uri::get_request_origin_uri;
use actix_web::HttpRequest;
use enums::by_table::users::user_signup_source::UserSignupSource;
use log::warn;
const ARTCRAFT : &str = "artcraft";
@@ -50,10 +49,6 @@ pub fn get_request_signup_source(http_request: &HttpRequest) -> Option<&'static
None
}
pub fn get_request_signup_source_enum(http_request: &HttpRequest) -> Option<UserSignupSource> {
get_request_signup_source(http_request)
.and_then(|source_str| UserSignupSource::from_str(source_str).ok())
}
#[cfg(test)]
mod tests {
@@ -61,7 +56,7 @@ mod tests {
mod origin_header_str {
use super::*;
use crate::http_server::requests::get_request_signup_source::get_request_signup_source;
use crate::requests::get_request_signup_source::get_request_signup_source;
#[test]
fn artcraft_dot_ai() {
@@ -119,66 +114,4 @@ mod tests {
assert_eq!(get_request_signup_source(&request), Some("storyteller"));
}
}
mod origin_header_enum {
use super::*;
use crate::http_server::requests::get_request_signup_source::get_request_signup_source_enum;
use enums::by_table::users::user_signup_source::UserSignupSource;
#[test]
fn artcraft_dot_ai() {
let request = TestRequest::get()
.insert_header(("origin", "https://artcraft.ai"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::ArtCraft));
}
#[test]
fn get_artcraft_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://getartcraft.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::ArtCraft));
}
#[test]
fn api_dot_get_artcraft_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://api.getartcraft.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::ArtCraft));
}
#[test]
fn fakeyou_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://fakeyou.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::FakeYou));
}
#[test]
fn api_dot_fakeyou_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://api.fakeyou.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::FakeYou));
}
#[test]
fn storyteller_dot_ai() {
let request = TestRequest::get()
.insert_header(("origin", "https://storyteller.ai"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::Storyteller));
}
#[test]
fn api_dot_storyteller_dot_ai() {
let request = TestRequest::get()
.insert_header(("origin", "https://api.storyteller.ai"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::Storyteller));
}
}
}
@@ -0,0 +1,75 @@
use crate::requests::get_request_signup_source::get_request_signup_source;
use actix_web::HttpRequest;
use enums::by_table::users::user_signup_source::UserSignupSource;
pub fn get_request_signup_source_enum(http_request: &HttpRequest) -> Option<UserSignupSource> {
get_request_signup_source(http_request)
.and_then(|source_str| UserSignupSource::from_str(source_str).ok())
}
#[cfg(test)]
mod tests {
use actix_web::test::TestRequest;
mod origin_header_enum {
use super::*;
use crate::requests::get_request_signup_source_enum::get_request_signup_source_enum;
use enums::by_table::users::user_signup_source::UserSignupSource;
#[test]
fn artcraft_dot_ai() {
let request = TestRequest::get()
.insert_header(("origin", "https://artcraft.ai"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::ArtCraft));
}
#[test]
fn get_artcraft_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://getartcraft.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::ArtCraft));
}
#[test]
fn api_dot_get_artcraft_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://api.getartcraft.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::ArtCraft));
}
#[test]
fn fakeyou_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://fakeyou.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::FakeYou));
}
#[test]
fn api_dot_fakeyou_dot_com() {
let request = TestRequest::get()
.insert_header(("origin", "https://api.fakeyou.com"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::FakeYou));
}
#[test]
fn storyteller_dot_ai() {
let request = TestRequest::get()
.insert_header(("origin", "https://storyteller.ai"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::Storyteller));
}
#[test]
fn api_dot_storyteller_dot_ai() {
let request = TestRequest::get()
.insert_header(("origin", "https://api.storyteller.ai"))
.to_http_request();
assert_eq!(get_request_signup_source_enum(&request), Some(UserSignupSource::Storyteller));
}
}
}
@@ -0,0 +1,2 @@
pub mod get_request_signup_source;
pub mod get_request_signup_source_enum;
@@ -0,0 +1,29 @@
use rand::Rng;
const EMAIL_PREFIX : &str = "synthetic.email";
const EMAIL_DOMAIN : &str = "getartcraft.com";
pub fn generate_random_synthetic_email() -> String {
let random_digit = rand::thread_rng().gen_range(u32::MIN..u32::MAX);
format!("{}.{}@{}", EMAIL_PREFIX, random_digit, EMAIL_DOMAIN)
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use crate::email::generate_random_synthetic_email::generate_random_synthetic_email;
#[test]
fn test_base_case_success() {
assert!(generate_random_synthetic_email().len() > 0);
}
#[test]
fn generate_lots() {
let mut collection = HashSet::new();
for _ in 0..100 {
collection.insert(generate_random_synthetic_email());
}
assert!(collection.len() > 50); // NB: Should be an easy bar to hit
}
}
+2 -1
View File
@@ -1 +1,2 @@
pub mod email_to_gravatar;
pub mod email_to_gravatar;
pub mod generate_random_synthetic_email;
@@ -21,6 +21,7 @@ normal = ["workspace-build-acceleration"]
workspace-build-acceleration.workspace = true
# Internal
actix_artcraft.workspace = true
artcraft_api_defs.workspace = true
component_traits.workspace = true
enums.workspace = true
@@ -1,15 +1,18 @@
use actix_web::HttpRequest;
use log::{info, warn};
use crate::utils::artcraft_stripe_config::ArtcraftStripeConfigWithClient;
use crate::utils::common_web_error::CommonWebError;
use sqlx::pool::PoolConnection;
use sqlx::MySql;
use stripe_shared::CheckoutSession;
use actix_artcraft::requests::get_request_signup_source_enum::get_request_signup_source_enum;
use actix_web::HttpRequest;
use http_server_common::request::get_request_ip::get_request_ip;
use log::{info, warn};
use mysql_queries::queries::users::user::create::create_account_error::CreateAccountError;
use mysql_queries::queries::users::user::create::create_account_from_google_sso::{create_account_from_google_sso, CreateAccountFromGoogleSsoArgs};
use mysql_queries::utils::transactor::Transactor;
use sqlx::pool::PoolConnection;
use sqlx::MySql;
use stripe_shared::CheckoutSession;
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
use users::email::email_to_gravatar::email_to_gravatar;
use users::email::generate_random_synthetic_email::generate_random_synthetic_email;
use users::username::generate_random_username::generate_random_username;
pub (super) async fn user_creation_case(
@@ -23,34 +26,43 @@ pub (super) async fn user_creation_case(
.await
.map_err(|e| {
warn!("Could not begin transaction: {:?}", e);
GoogleCreateAccountErrorResponse::server_error()
CommonWebError::ServerError
})?;
let ip_address = get_request_ip(&http_request);
let maybe_source = get_request_signup_source_enum(&http_request);
// NB: Not sure if these lock important functionality, so we're keeping this
let user_feature_flags = vec![
UserFeatureFlag::Studio,
UserFeatureFlag::VideoStyleTransfer,
].into_iter()
.map(|flag| flag.to_string())
.collect::<Vec<String>>()
.join(",");
let mut maybe_user_token = None;
let mut maybe_user_display_name = None;
for _ in 0..3 {
let user_email_address = "todo@todo.com";
// NB: We try a few times to make sure we don't hit an email/username collision.
let user_email_address = generate_random_synthetic_email();
let user_email_gravatar_hash = email_to_gravatar(&user_email_address);
// NB: We try a few times to make sure we don't hit a username collision.
let display_name = generate_random_username();
let username = display_name.trim().to_lowercase();
info!("generated username: {}", username);
info!("generated username: {} ; generated email: {}", username, user_email_address);
let result = create_account_from_google_sso(
CreateAccountFromGoogleSsoArgs {
username: &username,
display_name: &display_name,
email_address: &args.user_email_address,
email_address: &user_email_address,
email_gravatar_hash: &user_email_gravatar_hash,
email_confirmed_by_google: args.claims.email_verified(),
maybe_feature_flags: user_feature_flags.as_deref(),
maybe_feature_flags: Some(&user_feature_flags),
ip_address: &ip_address,
maybe_source,
},
@@ -68,7 +80,7 @@ pub (super) async fn user_creation_case(
},
Err(err) => {
warn!("error creating account from google sso: {:?}", err);
return Err(GoogleCreateAccountErrorResponse::server_error());
return Err(CommonWebError::ServerError);
},
}
}
@@ -32,8 +32,9 @@ path = "src/api_doc_cli.rs"
workspace-build-acceleration.workspace = true
# Internal
actix_artcraft.workspace = true
actix_cors_configs = { path = "../../../lib/actix_cors_configs" }
actix_helpers = { path = "../../../lib/actix_helpers" }
actix_helpers.workspace = true
artcraft_api_defs.workspace = true
bootstrap = { path = "../../../lib/bootstrap" }
bucket_paths = { path = "../../../lib/bucket_paths" }
@@ -1,7 +1,7 @@
use crate::http_server::endpoints::users::google_sso::google_sso_handler::GoogleCreateAccountErrorResponse;
use crate::http_server::endpoints::users::google_sso::handle_new_sso_account::NewSsoAccountInfo;
use crate::http_server::requests::get_request_signup_source::{get_request_signup_source, get_request_signup_source_enum};
use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags;
use actix_artcraft::requests::get_request_signup_source_enum::get_request_signup_source_enum;
use actix_web::HttpRequest;
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
use google_sign_in::claims::claims::Claims;
@@ -1,3 +1,2 @@
pub mod get_request_domain_branding;
pub mod get_request_signup_source;
pub mod request_headers;