mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-24 10:39:28 -04:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a569947608 | ||
|
|
9d66a880f9 | ||
|
|
7c2b0f310e | ||
|
|
d374317a77 | ||
|
|
f342befb0b | ||
|
|
13d1afd88d | ||
|
|
135dea926b | ||
|
|
4f68100c8f | ||
|
|
908f5cd3d6 | ||
|
|
5a57e48fcb | ||
|
|
69666c2646 | ||
|
|
4ed8b2111b | ||
|
|
d0fe9e1676 | ||
|
|
d455056c61 | ||
|
|
f17388c9de | ||
|
|
7665a07227 | ||
|
|
6dc55c1b62 | ||
|
|
0d565563b2 |
+102
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n j.status as `job_status: enums::common::job_status_plus::JobStatusPlus`,\n j.failure_reason as job_failure_reason,\n wle.credits_delta,\n wle.maybe_linked_refund_ledger_token as `maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,\n j.on_success_result_entity_token as `on_success_result_media_token: tokens::tokens::media_files::MediaFileToken`,\n j.token as `job_token: tokens::tokens::generic_inference_jobs::InferenceJobToken`,\n wle.token as `wallet_ledger_entry_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,\n wle.entry_type as `wallet_ledger_entry_type: enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType`\nFROM generic_inference_jobs as j\nLEFT OUTER JOIN users as u\n ON j.maybe_creator_user_token = u.token\nLEFT OUTER JOIN wallet_ledger_entries as wle\n ON wle.maybe_entity_ref = j.token\nWHERE u.token = ?\nORDER BY j.id DESC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "job_status: enums::common::job_status_plus::JobStatusPlus",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 76
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "job_failure_reason",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 2048
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "credits_delta",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "",
|
||||
"char_set": 63,
|
||||
"max_size": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "on_success_result_media_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "job_token: tokens::tokens::generic_inference_jobs::InferenceJobToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "wallet_ledger_entry_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "wallet_ledger_entry_type: enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "0757d81ba153b2ad2df25df3dd92b655ef08b95f5f83251bf3573f9be3a9f8d1"
|
||||
}
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO users\nSET\n token = ?,\n \n is_temporary = ?,\n \n username = ?,\n display_name = ?,\n\n username_is_generated = ?,\n username_is_not_customized = ?,\n\n email_address = ?,\n email_gravatar_hash = ?,\n\n email_confirmed = FALSE,\n email_confirmed_by_google = ?,\n email_is_synthetic = ?,\n \n was_eagerly_provisioned = ?,\n\n profile_markdown = ?,\n profile_rendered_html = ?,\n user_role_slug = ?,\n\n password_hash = ?,\n is_without_password = ?,\n\n maybe_feature_flags = ?,\n\n ip_address_creation = ?,\n ip_address_last_login = ?,\n ip_address_last_update = ?,\n\n maybe_source = ?,\n\n maybe_signup_method = ?\n ",
|
||||
"query": "\nINSERT INTO users\nSET\n token = ?,\n \n is_temporary = ?,\n \n username = ?,\n display_name = ?,\n\n username_is_generated = ?,\n username_is_not_customized = ?,\n\n email_address = ?,\n email_gravatar_hash = ?,\n\n email_confirmed = FALSE,\n email_confirmed_by_google = ?,\n email_is_synthetic = ?,\n \n was_eagerly_provisioned = ?,\n\n profile_markdown = ?,\n profile_rendered_html = ?,\n user_role_slug = ?,\n\n password_hash = ?,\n is_without_password = ?,\n\n maybe_feature_flags = ?,\n\n ip_address_creation = ?,\n ip_address_last_login = ?,\n ip_address_last_update = ?,\n\n maybe_source = ?,\n\n maybe_signup_method = ?,\n\n maybe_referral_url = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 22
|
||||
"Right": 23
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "56746bcb993cb33f5492f401ce4fc3fd44d91fed660c2f106cb22e482dffa740"
|
||||
"hash": "08996721482fe897c2721298c04cf0eba5fcf1d04d57397b8afab3dd59feaa8c"
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n user_subscriptions.subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n user_subscriptions.maybe_stripe_subscription_id,\n users.token as `user_token: tokens::tokens::users::UserToken`,\n users.email_address,\n users.username,\n users.display_name\nFROM user_subscriptions\nINNER JOIN users ON users.token = user_subscriptions.user_token\nWHERE\n user_subscriptions.maybe_stripe_customer_id = ?\n AND user_subscriptions.deleted_at IS NULL\n AND users.user_deleted_at IS NULL\n AND users.mod_deleted_at IS NULL\nORDER BY user_subscriptions.created_at DESC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "subscription_namespace: enums::common::payments_namespace::PaymentsNamespace",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "maybe_stripe_subscription_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "email_address",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3390084f700ef2444691a02c691a59ccdd66bc8b096547e89c68b58b04dd44e4"
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n w.token as `token: tokens::tokens::wallets::WalletToken`,\n w.wallet_namespace as `wallet_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n w.owner_user_token as `owner_user_token: tokens::tokens::users::UserToken`,\n w.banked_credits as `banked_credits: u32`,\n w.monthly_credits as `monthly_credits: u32`,\n w.version,\n w.created_at as `created_at: DateTime<Utc>`,\n w.updated_at as `updated_at: DateTime<Utc>`\nFROM wallets as w\nWHERE w.token = ?\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::wallets::WalletToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "wallet_namespace: enums::common::payments_namespace::PaymentsNamespace",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "owner_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "banked_credits: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "monthly_credits: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "version",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "created_at: DateTime<Utc>",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "updated_at: DateTime<Utc>",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "35b09082ebb923c8c3191eaee8553b7c3badbd9152ee43999f7c8ff1a6410fe9"
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n w.token as `token: tokens::tokens::wallets::WalletToken`,\n w.wallet_namespace as `wallet_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n w.banked_credits as `banked_credits: u32`,\n w.monthly_credits as `monthly_credits: u32`,\n w.version,\n w.created_at as `created_at: DateTime<Utc>`,\n w.updated_at as `updated_at: DateTime<Utc>`\nFROM wallets as w\nWHERE w.owner_user_token = ?\nORDER BY w.id DESC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::wallets::WalletToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "wallet_namespace: enums::common::payments_namespace::PaymentsNamespace",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "banked_credits: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "monthly_credits: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "version",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "created_at: DateTime<Utc>",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "updated_at: DateTime<Utc>",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "69b6e933ad400bc574204de301dde6748d2a1723f47c12e86afc05033a60d3a2"
|
||||
}
|
||||
+146
@@ -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<Utc>`,\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<Utc>",
|
||||
"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"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n INSERT INTO web_referrals (\n url,\n domain,\n utm_source,\n utm_medium,\n utm_campaign,\n ip_address,\n anonymous_visitor_token\n ) VALUES (?, ?, ?, ?, ?, ?, ?)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 7
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "80f73702374593c141d183d86bd8af73d37fd7118914d0ba12a02f53c1b84c78"
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n users.token as `user_token: tokens::tokens::users::UserToken`,\n username,\n display_name,\n username_is_generated as `username_is_generated: bool`,\n is_temporary as `is_temporary: bool`,\n username_is_not_customized as `username_is_not_customized: bool`,\n email_address,\n email_confirmed as `email_confirmed: bool`,\n email_is_synthetic as `email_is_synthetic: bool`,\n is_without_password as `is_without_password: bool`,\n ip_address_creation,\n ip_address_last_login,\n maybe_avatar_media_file_token as `maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n email_gravatar_hash,\n is_banned as `is_banned: bool`,\n created_at,\n updated_at\nFROM users\nWHERE\n users.username = ?\n AND users.user_deleted_at IS NULL\n AND users.mod_deleted_at IS NULL\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "username_is_generated: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "is_temporary: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "username_is_not_customized: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email_address",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "email_confirmed: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "email_is_synthetic: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "is_without_password: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "ip_address_creation",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "ip_address_last_login",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "email_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "is_banned: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "97599bdc5467b8750d2546cc6f78457c26d93c3be52921ad9b3531fd88498194"
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,\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 is_refunded as `is_refunded: bool`,\n maybe_linked_refund_ledger_token as `maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,\n created_at as `created_at: DateTime<Utc>`\nFROM wallet_ledger_entries\nWHERE token = ?\nLIMIT 1\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": "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": 2,
|
||||
"name": "maybe_entity_ref",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "credits_delta",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "banked_credits_before: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "banked_credits_after: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "monthly_credits_before: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "monthly_credits_after: u32",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "is_refunded: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "created_at: DateTime<Utc>",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "9c28e9f8f163407755acaca1fb4a72849679b6bf98b8c87e1761200300111232"
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n users.token as `user_token: tokens::tokens::users::UserToken`,\n username,\n display_name,\n username_is_generated as `username_is_generated: bool`,\n is_temporary as `is_temporary: bool`,\n username_is_not_customized as `username_is_not_customized: bool`,\n email_address,\n email_confirmed as `email_confirmed: bool`,\n email_is_synthetic as `email_is_synthetic: bool`,\n is_without_password as `is_without_password: bool`,\n ip_address_creation,\n ip_address_last_login,\n maybe_avatar_media_file_token as `maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n email_gravatar_hash,\n is_banned as `is_banned: bool`,\n created_at,\n updated_at\nFROM users\nWHERE\n users.email_address = ?\n AND users.user_deleted_at IS NULL\n AND users.mod_deleted_at IS NULL\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "username_is_generated: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "is_temporary: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "username_is_not_customized: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email_address",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "email_confirmed: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "email_is_synthetic: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "is_without_password: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "ip_address_creation",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "ip_address_last_login",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "email_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "is_banned: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d023eb33c9a06f8b07b1e5d1e255dbb730244bbd764a49d519473e50751a3ddc"
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n users.token as `user_token: tokens::tokens::users::UserToken`,\n username,\n display_name,\n username_is_generated as `username_is_generated: bool`,\n is_temporary as `is_temporary: bool`,\n username_is_not_customized as `username_is_not_customized: bool`,\n email_address,\n email_confirmed as `email_confirmed: bool`,\n email_is_synthetic as `email_is_synthetic: bool`,\n is_without_password as `is_without_password: bool`,\n ip_address_creation,\n ip_address_last_login,\n maybe_avatar_media_file_token as `maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n email_gravatar_hash,\n is_banned as `is_banned: bool`,\n created_at,\n updated_at\nFROM users\nWHERE\n users.token = ?\n AND users.user_deleted_at IS NULL\n AND users.mod_deleted_at IS NULL\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "username_is_generated: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "is_temporary: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "username_is_not_customized: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email_address",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "email_confirmed: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "email_is_synthetic: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "is_without_password: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "ip_address_creation",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "ip_address_last_login",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "email_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "is_banned: bool",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "fd92483d9f1849d5b7f04076446fa98688fec46bb1cba20d005bc8dad4d1c093"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE users
|
||||
DROP COLUMN
|
||||
maybe_referral_url;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE users
|
||||
ADD COLUMN
|
||||
maybe_referral_url VARCHAR(255) DEFAULT NULL;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
DROP TABLE web_referrals;
|
||||
@@ -0,0 +1,27 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Records of web referrals
|
||||
CREATE TABLE web_referrals (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- The referral URL
|
||||
url VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The parsed out referral domain
|
||||
domain VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
utm_source VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
utm_medium VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
utm_campaign VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE web_referrals
|
||||
DROP COLUMN ip_address,
|
||||
DROP COLUMN anonymous_visitor_token;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE web_referrals
|
||||
ADD COLUMN ip_address VARCHAR(40) NOT NULL,
|
||||
ADD COLUMN anonymous_visitor_token VARCHAR(32) DEFAULT NULL;
|
||||
@@ -220,6 +220,9 @@ CREATE TABLE users (
|
||||
-- statistical evaluation.)
|
||||
was_eagerly_provisioned BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- The referral URL the user arrived from when signing up.
|
||||
maybe_referral_url VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- ========== MODERATION DETAILS ==========
|
||||
|
||||
-- Different than deleted.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Records of web referrals
|
||||
CREATE TABLE web_referrals (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- The referral URL
|
||||
url VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The parsed out referral domain
|
||||
domain VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
utm_source VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
utm_medium VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
utm_campaign VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -4,8 +4,10 @@ pub mod credits;
|
||||
pub mod generate;
|
||||
pub mod jobs;
|
||||
pub mod media_file;
|
||||
pub mod moderation;
|
||||
pub mod prompts;
|
||||
pub mod stripe_artcraft;
|
||||
pub mod subscriptions;
|
||||
pub mod users;
|
||||
pub mod utils;
|
||||
pub mod utils;
|
||||
pub mod web_referrals;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod user;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
use enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const LIST_USER_JOBS_PATH: &str = "/v1/moderation/jobs/user/{user_token}/list";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ListUserJobsPathInfo {
|
||||
pub user_token: UserToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListUserJobsResponse {
|
||||
pub success: bool,
|
||||
pub jobs: Vec<ListUserJobsEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListUserJobsEntry {
|
||||
pub job_status: JobStatusPlus,
|
||||
pub job_failure_reason: Option<String>,
|
||||
pub credits_delta: Option<i32>,
|
||||
pub maybe_linked_refund_ledger_token: Option<WalletLedgerEntryToken>,
|
||||
pub on_success_result_media_token: Option<MediaFileToken>,
|
||||
pub job_token: InferenceJobToken,
|
||||
pub wallet_ledger_entry_token: Option<WalletLedgerEntryToken>,
|
||||
pub wallet_ledger_entry_type: Option<WalletLedgerEntryType>,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod list_user_jobs;
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod jobs;
|
||||
pub mod user;
|
||||
pub mod wallet_ledger_entries;
|
||||
pub mod wallets;
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod user_lookup;
|
||||
pub mod user_lookup_by_stripe_customer_id;
|
||||
@@ -0,0 +1,39 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const MODERATOR_USER_LOOKUP_PATH: &str = "/v1/moderation/users/lookup";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModeratorUserLookupRequest {
|
||||
pub search: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorUserLookupSuccessResponse {
|
||||
pub success: bool,
|
||||
pub maybe_user: Option<ModeratorUserLookupUserDetails>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorUserLookupUserDetails {
|
||||
pub token: UserToken,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub username_is_generated: bool,
|
||||
pub is_temporary: bool,
|
||||
pub username_is_not_customized: bool,
|
||||
pub email_address: String,
|
||||
pub email_confirmed: bool,
|
||||
pub email_is_synthetic: bool,
|
||||
pub is_without_password: bool,
|
||||
pub ip_address_creation: String,
|
||||
pub ip_address_last_login: String,
|
||||
pub maybe_avatar_media_file_token: Option<MediaFileToken>,
|
||||
pub email_gravatar_hash: String,
|
||||
pub is_banned: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::users::UserToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const MODERATOR_USER_LOOKUP_BY_STRIPE_CUSTOMER_ID_PATH: &str = "/v1/moderation/users/lookup_by_stripe_customer_id";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModeratorUserLookupByStripeCustomerIdRequest {
|
||||
pub stripe_customer_id: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorUserLookupByStripeCustomerIdResponse {
|
||||
pub success: bool,
|
||||
pub users: Vec<ModeratorUserLookupByStripeCustomerIdEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorUserLookupByStripeCustomerIdEntry {
|
||||
pub subscription_namespace: PaymentsNamespace,
|
||||
pub maybe_stripe_subscription_id: Option<String>,
|
||||
pub token: UserToken,
|
||||
pub email_address: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
}
|
||||
+35
@@ -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<ListWalletLedgerEntriesByWalletEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListWalletLedgerEntriesByWalletEntry {
|
||||
pub token: WalletLedgerEntryToken,
|
||||
pub wallet_token: WalletToken,
|
||||
pub entry_type: WalletLedgerEntryType,
|
||||
pub maybe_entity_ref: Option<String>,
|
||||
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<Utc>,
|
||||
pub is_refunded: bool,
|
||||
pub maybe_linked_refund_ledger_token: Option<WalletLedgerEntryToken>,
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
pub mod list_wallet_ledger_entries_by_wallet;
|
||||
pub mod moderator_get_wallet_ledger_entry;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
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 utoipa::ToSchema;
|
||||
|
||||
pub const MODERATOR_GET_WALLET_LEDGER_ENTRY_PATH: &str = "/v1/moderation/wallet_ledger_entry/{wallet_ledger_entry_token}";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModeratorGetWalletLedgerEntryPathInfo {
|
||||
pub wallet_ledger_entry_token: WalletLedgerEntryToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorGetWalletLedgerEntryResponse {
|
||||
pub success: bool,
|
||||
pub maybe_entry: Option<ModeratorGetWalletLedgerEntryDetails>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorGetWalletLedgerEntryDetails {
|
||||
pub token: WalletLedgerEntryToken,
|
||||
pub entry_type: WalletLedgerEntryType,
|
||||
pub maybe_entity_ref: Option<String>,
|
||||
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 is_refunded: bool,
|
||||
pub maybe_linked_refund_ledger_token: Option<WalletLedgerEntryToken>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const LIST_USER_WALLETS_PATH: &str = "/v1/moderation/wallets/user/{user_token}/list";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ListUserWalletsPathInfo {
|
||||
pub user_token: UserToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListUserWalletsResponse {
|
||||
pub success: bool,
|
||||
pub wallets: Vec<ListUserWalletsEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListUserWalletsEntry {
|
||||
pub token: WalletToken,
|
||||
pub wallet_namespace: PaymentsNamespace,
|
||||
pub banked_credits: u32,
|
||||
pub monthly_credits: u32,
|
||||
pub version: i32,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod list_user_wallets;
|
||||
pub mod moderator_add_banked_balance_to_wallet;
|
||||
pub mod moderator_get_wallet;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const MODERATOR_ADD_BANKED_BALANCE_TO_WALLET_PATH: &str = "/v1/moderation/wallet/{wallet_token}/add_banked_balance";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModeratorAddBankedBalanceToWalletPathInfo {
|
||||
pub wallet_token: WalletToken,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModeratorAddBankedBalanceToWalletRequest {
|
||||
pub credits: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorAddBankedBalanceToWalletResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const MODERATOR_GET_WALLET_PATH: &str = "/v1/moderation/wallet/{wallet_token}";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct ModeratorGetWalletPathInfo {
|
||||
pub wallet_token: WalletToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorGetWalletResponse {
|
||||
pub success: bool,
|
||||
pub maybe_wallet: Option<ModeratorGetWalletDetails>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ModeratorGetWalletDetails {
|
||||
pub token: WalletToken,
|
||||
pub wallet_namespace: PaymentsNamespace,
|
||||
pub owner_user_token: UserToken,
|
||||
pub banked_credits: u32,
|
||||
pub monthly_credits: u32,
|
||||
pub version: i32,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
+5
@@ -11,6 +11,11 @@ pub struct StripeArtcraftCreateSubscriptionCheckoutWithUserSignupRequest {
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
|
||||
pub cadence: Option<PlanBillingCadence>,
|
||||
|
||||
/// Optional: The referral URL the user arrived from when signing up.
|
||||
/// The browser can send `document.referrer` to the backend so we know how people are finding us.
|
||||
/// If the browser doesn't send this parameter, we'll try the `referer` header.
|
||||
pub maybe_referral_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug, Copy, Clone)]
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const LOG_WEB_REFERRAL_URL_PATH: &str = "/v1/web_referrals/record";
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct LogWebReferralRequest {
|
||||
/// Optional: The referral URL the user arrived from when signing up.
|
||||
/// The browser can send `document.referrer` to the backend so we know how people are finding us.
|
||||
/// If the browser doesn't send this parameter, we'll try the `referer` header.
|
||||
pub maybe_referral_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct LogWebReferralResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod log_web_referral;
|
||||
@@ -62,6 +62,7 @@ async fn seed_user(
|
||||
password_hash: &password_hash,
|
||||
ip_address: "127.0.0.1",
|
||||
maybe_source: None,
|
||||
maybe_referral_url: None,
|
||||
maybe_user_token: maybe_user_token.as_ref(),
|
||||
}).await.map_err(|err| anyhow!("err: {:?}", err))?;
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use actix_cors::Cors;
|
||||
use crate::util::netlify_branch_domain_matches::netlify_branch_domain_matches;
|
||||
|
||||
pub fn add_artcraft_admin_dashboard(cors: Cors, _is_production: bool) -> Cors {
|
||||
cors
|
||||
.allowed_origin("https://artcraft-dashboard.netlify.app")
|
||||
.allowed_origin_fn(|origin, _req_head| {
|
||||
netlify_branch_domain_matches(origin, "artcraft-dashboard.netlify.app")
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub (crate) mod artcraft_admin_dashboard;
|
||||
pub (crate) mod artcraft_2d;
|
||||
pub (crate) mod artcraft_3d;
|
||||
pub (crate) mod artcraft_website;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use actix_cors::Cors;
|
||||
use log::info;
|
||||
|
||||
use crate::configs::artcraft_admin_dashboard::add_artcraft_admin_dashboard;
|
||||
use crate::configs::artcraft_2d::add_artcraft_2d;
|
||||
use crate::configs::artcraft_3d::add_artcraft_3d;
|
||||
use crate::configs::artcraft_website::add_artcraft_website;
|
||||
@@ -48,6 +49,7 @@ fn do_build_cors_config(is_production: bool) -> Cors {
|
||||
cors = add_artcraft_2d(cors, is_production);
|
||||
cors = add_artcraft_3d(cors, is_production);
|
||||
cors = add_artcraft_website(cors, is_production);
|
||||
cors = add_artcraft_admin_dashboard(cors, is_production);
|
||||
|
||||
// Legacy
|
||||
cors = add_legacy_trumped(cors, is_production);
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
use anyhow::anyhow;
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken;
|
||||
|
||||
pub struct UserJobForModerationResult {
|
||||
pub job_status: JobStatusPlus,
|
||||
pub job_failure_reason: Option<String>,
|
||||
pub credits_delta: Option<i32>,
|
||||
pub maybe_linked_refund_ledger_token: Option<WalletLedgerEntryToken>,
|
||||
pub on_success_result_media_token: Option<MediaFileToken>,
|
||||
pub job_token: InferenceJobToken,
|
||||
pub wallet_ledger_entry_token: Option<WalletLedgerEntryToken>,
|
||||
pub wallet_ledger_entry_type: Option<WalletLedgerEntryType>,
|
||||
}
|
||||
|
||||
pub async fn list_user_jobs_for_moderation(
|
||||
user_token: &UserToken,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Vec<UserJobForModerationResult>> {
|
||||
let results = sqlx::query_as!(
|
||||
UserJobForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
j.status as `job_status: enums::common::job_status_plus::JobStatusPlus`,
|
||||
j.failure_reason as job_failure_reason,
|
||||
wle.credits_delta,
|
||||
wle.maybe_linked_refund_ledger_token as `maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,
|
||||
j.on_success_result_entity_token as `on_success_result_media_token: tokens::tokens::media_files::MediaFileToken`,
|
||||
j.token as `job_token: tokens::tokens::generic_inference_jobs::InferenceJobToken`,
|
||||
wle.token as `wallet_ledger_entry_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,
|
||||
wle.entry_type as `wallet_ledger_entry_type: enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType`
|
||||
FROM generic_inference_jobs as j
|
||||
LEFT OUTER JOIN users as u
|
||||
ON j.maybe_creator_user_token = u.token
|
||||
LEFT OUTER JOIN wallet_ledger_entries as wle
|
||||
ON wle.maybe_entity_ref = j.token
|
||||
WHERE u.token = ?
|
||||
ORDER BY j.id DESC
|
||||
"#,
|
||||
user_token,
|
||||
)
|
||||
.fetch_all(mysql_pool)
|
||||
.await;
|
||||
|
||||
match results {
|
||||
Ok(records) => Ok(records),
|
||||
Err(err) => {
|
||||
warn!("list_user_jobs_for_moderation query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ pub mod get_pending_inference_job_count;
|
||||
pub mod insert_generic_inference_job;
|
||||
pub mod job_status;
|
||||
pub mod kill_generic_inference_jobs;
|
||||
pub mod list_user_jobs_for_moderation;
|
||||
pub mod kill_jobs_in_development;
|
||||
pub mod list_session_jobs;
|
||||
pub mod mark_generic_inference_job_cancelled_by_user;
|
||||
|
||||
@@ -39,4 +39,5 @@ pub mod voice_conversion;
|
||||
pub mod voice_designer;
|
||||
pub mod w2l;
|
||||
pub mod wallet_ledger_entries;
|
||||
pub mod wallets;
|
||||
pub mod wallets;
|
||||
pub mod web_referrals;
|
||||
+8
-1
@@ -20,6 +20,11 @@ pub struct CreateAccountFromEmailPasswordArgs<'a> {
|
||||
pub ip_address: &'a str,
|
||||
pub maybe_source: Option<UserSignupSource>,
|
||||
|
||||
/// The referral URL the user arrived from when signing up.
|
||||
/// The browser can send `document.referrer` to the backend so we know how people are finding us.
|
||||
/// If the browser doesn't send this parameter, we'll try the `referer` header.
|
||||
pub maybe_referral_url: Option<String>,
|
||||
|
||||
/// In production code, send this as `None`.
|
||||
/// Only provide an external user token for db integration tests and db seeding tools.
|
||||
/// This allows for knowing the user token a priori.
|
||||
@@ -62,9 +67,11 @@ pub async fn create_account_from_email_and_password(
|
||||
password_hash: args.password_hash,
|
||||
is_without_password: false,
|
||||
|
||||
maybe_referral_url: args.maybe_referral_url,
|
||||
|
||||
// NB: This is just for testing.
|
||||
maybe_user_token: args.maybe_user_token,
|
||||
|
||||
|
||||
// These flags represent eagerly created users and are false for this flow
|
||||
is_temporary: false,
|
||||
email_is_synthetic: false,
|
||||
|
||||
+3
@@ -57,6 +57,9 @@ pub async fn create_account_from_google_sso(
|
||||
password_hash: SSO_PASSWORD,
|
||||
is_without_password: true,
|
||||
|
||||
// TODO: Necessary? We're not doing SSO for now.
|
||||
maybe_referral_url: None,
|
||||
|
||||
// NB: This is just for testing.
|
||||
maybe_user_token: None,
|
||||
|
||||
|
||||
+3
@@ -26,6 +26,7 @@ pub struct CreateAccountFromStripeCheckoutArgs<'a> {
|
||||
|
||||
pub ip_address: &'a str,
|
||||
pub maybe_source: Option<UserSignupSource>,
|
||||
pub maybe_referral_url: Option<String>,
|
||||
|
||||
/// Comma separated string of feature flags.
|
||||
pub maybe_feature_flags: Option<&'a str>,
|
||||
@@ -68,6 +69,8 @@ pub async fn create_account_from_stripe_checkout(
|
||||
password_hash: SSO_PASSWORD,
|
||||
is_without_password: true,
|
||||
|
||||
maybe_referral_url: args.maybe_referral_url,
|
||||
|
||||
// NB: This is just for testing.
|
||||
maybe_user_token: None,
|
||||
},
|
||||
|
||||
+10
-1
@@ -60,6 +60,11 @@ pub struct GenericCreateAccountArgs<'a> {
|
||||
/// Comma separated string of feature flags.
|
||||
pub maybe_feature_flags: Option<&'a str>,
|
||||
|
||||
/// The referral URL the user arrived from when signing up.
|
||||
/// The browser can send `document.referrer` to the backend so we know how people are finding us.
|
||||
/// If the browser doesn't send this parameter, we'll try the `referer` header.
|
||||
pub maybe_referral_url: Option<String>,
|
||||
|
||||
/// In production code, send this as `None`.
|
||||
/// Only provide an external user token for db integration tests and db seeding tools.
|
||||
/// This allows for knowing the user token a priori.
|
||||
@@ -124,7 +129,9 @@ SET
|
||||
|
||||
maybe_source = ?,
|
||||
|
||||
maybe_signup_method = ?
|
||||
maybe_signup_method = ?,
|
||||
|
||||
maybe_referral_url = ?
|
||||
"#,
|
||||
user_token.as_str(),
|
||||
|
||||
@@ -159,6 +166,8 @@ SET
|
||||
|
||||
args.maybe_source.map(|s| s.to_str()),
|
||||
args.maybe_signup_method.map(|m| m.to_str()),
|
||||
|
||||
args.maybe_referral_url,
|
||||
);
|
||||
|
||||
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub struct LookupUserForModerationResult {
|
||||
pub user_token: UserToken,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub username_is_generated: bool,
|
||||
pub is_temporary: bool,
|
||||
pub username_is_not_customized: bool,
|
||||
pub email_address: String,
|
||||
pub email_confirmed: bool,
|
||||
pub email_is_synthetic: bool,
|
||||
pub is_without_password: bool,
|
||||
pub ip_address_creation: String,
|
||||
pub ip_address_last_login: String,
|
||||
pub maybe_avatar_media_file_token: Option<MediaFileToken>,
|
||||
pub email_gravatar_hash: String,
|
||||
pub is_banned: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn lookup_user_for_moderation_by_token(
|
||||
token: &str,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<LookupUserForModerationResult>> {
|
||||
let result = sqlx::query_as!(
|
||||
LookupUserForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
users.token as `user_token: tokens::tokens::users::UserToken`,
|
||||
username,
|
||||
display_name,
|
||||
username_is_generated as `username_is_generated: bool`,
|
||||
is_temporary as `is_temporary: bool`,
|
||||
username_is_not_customized as `username_is_not_customized: bool`,
|
||||
email_address,
|
||||
email_confirmed as `email_confirmed: bool`,
|
||||
email_is_synthetic as `email_is_synthetic: bool`,
|
||||
is_without_password as `is_without_password: bool`,
|
||||
ip_address_creation,
|
||||
ip_address_last_login,
|
||||
maybe_avatar_media_file_token as `maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken`,
|
||||
email_gravatar_hash,
|
||||
is_banned as `is_banned: bool`,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM users
|
||||
WHERE
|
||||
users.token = ?
|
||||
AND users.user_deleted_at IS NULL
|
||||
AND users.mod_deleted_at IS NULL
|
||||
LIMIT 1
|
||||
"#,
|
||||
token,
|
||||
)
|
||||
.fetch_one(mysql_pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(record) => Ok(Some(record)),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(err) => {
|
||||
warn!("lookup_user_for_moderation_by_token query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn lookup_user_for_moderation_by_email(
|
||||
email: &str,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<LookupUserForModerationResult>> {
|
||||
let email = email.trim().to_lowercase();
|
||||
|
||||
let result = sqlx::query_as!(
|
||||
LookupUserForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
users.token as `user_token: tokens::tokens::users::UserToken`,
|
||||
username,
|
||||
display_name,
|
||||
username_is_generated as `username_is_generated: bool`,
|
||||
is_temporary as `is_temporary: bool`,
|
||||
username_is_not_customized as `username_is_not_customized: bool`,
|
||||
email_address,
|
||||
email_confirmed as `email_confirmed: bool`,
|
||||
email_is_synthetic as `email_is_synthetic: bool`,
|
||||
is_without_password as `is_without_password: bool`,
|
||||
ip_address_creation,
|
||||
ip_address_last_login,
|
||||
maybe_avatar_media_file_token as `maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken`,
|
||||
email_gravatar_hash,
|
||||
is_banned as `is_banned: bool`,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM users
|
||||
WHERE
|
||||
users.email_address = ?
|
||||
AND users.user_deleted_at IS NULL
|
||||
AND users.mod_deleted_at IS NULL
|
||||
LIMIT 1
|
||||
"#,
|
||||
email,
|
||||
)
|
||||
.fetch_one(mysql_pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(record) => Ok(Some(record)),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(err) => {
|
||||
warn!("lookup_user_for_moderation_by_email query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn lookup_user_for_moderation_by_username(
|
||||
username: &str,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<LookupUserForModerationResult>> {
|
||||
let username = username.trim().to_lowercase();
|
||||
|
||||
let result = sqlx::query_as!(
|
||||
LookupUserForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
users.token as `user_token: tokens::tokens::users::UserToken`,
|
||||
username,
|
||||
display_name,
|
||||
username_is_generated as `username_is_generated: bool`,
|
||||
is_temporary as `is_temporary: bool`,
|
||||
username_is_not_customized as `username_is_not_customized: bool`,
|
||||
email_address,
|
||||
email_confirmed as `email_confirmed: bool`,
|
||||
email_is_synthetic as `email_is_synthetic: bool`,
|
||||
is_without_password as `is_without_password: bool`,
|
||||
ip_address_creation,
|
||||
ip_address_last_login,
|
||||
maybe_avatar_media_file_token as `maybe_avatar_media_file_token: tokens::tokens::media_files::MediaFileToken`,
|
||||
email_gravatar_hash,
|
||||
is_banned as `is_banned: bool`,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM users
|
||||
WHERE
|
||||
users.username = ?
|
||||
AND users.user_deleted_at IS NULL
|
||||
AND users.mod_deleted_at IS NULL
|
||||
LIMIT 1
|
||||
"#,
|
||||
username,
|
||||
)
|
||||
.fetch_one(mysql_pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(record) => Ok(Some(record)),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(err) => {
|
||||
warn!("lookup_user_for_moderation_by_username query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,3 +3,4 @@ pub mod lookup_user_for_login_by_email;
|
||||
pub mod lookup_user_for_login_by_email_with_transactor;
|
||||
pub mod lookup_user_for_login_by_username;
|
||||
pub mod lookup_user_for_login_result;
|
||||
pub mod lookup_user_for_moderation;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod edit_user_profile_as_account_holder;
|
||||
pub mod edit_user_profile_as_mod;
|
||||
pub mod get_user_profile_by_token;
|
||||
pub mod get_user_profile_by_username;
|
||||
pub mod get_user_profile_by_username;
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
use anyhow::anyhow;
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub struct UserByStripeCustomerIdResult {
|
||||
pub subscription_namespace: PaymentsNamespace,
|
||||
pub maybe_stripe_subscription_id: Option<String>,
|
||||
pub user_token: UserToken,
|
||||
pub email_address: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
}
|
||||
|
||||
pub async fn lookup_users_by_stripe_customer_id(
|
||||
stripe_customer_id: &str,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Vec<UserByStripeCustomerIdResult>> {
|
||||
let results = sqlx::query_as!(
|
||||
UserByStripeCustomerIdResult,
|
||||
r#"
|
||||
SELECT
|
||||
user_subscriptions.subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,
|
||||
user_subscriptions.maybe_stripe_subscription_id,
|
||||
users.token as `user_token: tokens::tokens::users::UserToken`,
|
||||
users.email_address,
|
||||
users.username,
|
||||
users.display_name
|
||||
FROM user_subscriptions
|
||||
INNER JOIN users ON users.token = user_subscriptions.user_token
|
||||
WHERE
|
||||
user_subscriptions.maybe_stripe_customer_id = ?
|
||||
AND user_subscriptions.deleted_at IS NULL
|
||||
AND users.user_deleted_at IS NULL
|
||||
AND users.mod_deleted_at IS NULL
|
||||
ORDER BY user_subscriptions.created_at DESC
|
||||
"#,
|
||||
stripe_customer_id,
|
||||
)
|
||||
.fetch_all(mysql_pool)
|
||||
.await;
|
||||
|
||||
match results {
|
||||
Ok(records) => Ok(records),
|
||||
Err(err) => {
|
||||
warn!("lookup_users_by_stripe_customer_id query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod find_possibly_inactive_first_subscription_for_owner_user;
|
||||
pub mod lookup_users_by_stripe_customer_id;
|
||||
pub mod find_subscription_for_owner_user;
|
||||
pub mod get_user_subscription_by_stripe_subscription_id;
|
||||
pub mod list_active_user_subscriptions;
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
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;
|
||||
|
||||
pub struct WalletLedgerEntryForModerationResult {
|
||||
pub token: WalletLedgerEntryToken,
|
||||
pub entry_type: WalletLedgerEntryType,
|
||||
pub maybe_entity_ref: Option<String>,
|
||||
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 is_refunded: bool,
|
||||
pub maybe_linked_refund_ledger_token: Option<WalletLedgerEntryToken>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn get_wallet_ledger_entry_for_moderation(
|
||||
wallet_ledger_entry_token: &WalletLedgerEntryToken,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<WalletLedgerEntryForModerationResult>> {
|
||||
let result = sqlx::query_as!(
|
||||
WalletLedgerEntryForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
token as `token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,
|
||||
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`,
|
||||
is_refunded as `is_refunded: bool`,
|
||||
maybe_linked_refund_ledger_token as `maybe_linked_refund_ledger_token: tokens::tokens::wallet_ledger_entries::WalletLedgerEntryToken`,
|
||||
created_at as `created_at: DateTime<Utc>`
|
||||
FROM wallet_ledger_entries
|
||||
WHERE token = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
wallet_ledger_entry_token,
|
||||
)
|
||||
.fetch_one(mysql_pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(record) => Ok(Some(record)),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(err) => {
|
||||
warn!("get_wallet_ledger_entry_for_moderation query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
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;
|
||||
|
||||
// TODO(bt): This will need to be paginated. Sort direction would be nice, too.
|
||||
|
||||
pub struct WalletLedgerEntryForModerationResult {
|
||||
pub token: WalletLedgerEntryToken,
|
||||
pub wallet_token: WalletToken,
|
||||
pub entry_type: WalletLedgerEntryType,
|
||||
pub maybe_entity_ref: Option<String>,
|
||||
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<Utc>,
|
||||
pub is_refunded: bool,
|
||||
pub maybe_linked_refund_ledger_token: Option<WalletLedgerEntryToken>,
|
||||
}
|
||||
|
||||
pub async fn list_wallet_ledger_entries_by_wallet(
|
||||
wallet_token: &WalletToken,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Vec<WalletLedgerEntryForModerationResult>> {
|
||||
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<Utc>`,
|
||||
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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
pub (crate) mod internal_insert_wallet_ledger_entry;
|
||||
pub mod internal_insert_wallet_created_ledger_entry;
|
||||
pub mod internal_insert_wallet_created_ledger_entry;
|
||||
pub mod get_wallet_ledger_entry_for_moderation;
|
||||
pub mod list_wallet_ledger_entries_by_wallet;
|
||||
+2
-2
@@ -10,6 +10,7 @@ pub async fn add_durable_banked_balance_to_wallet(
|
||||
wallet_token: &WalletToken,
|
||||
amount_to_add: u64,
|
||||
maybe_ledger_ref: Option<&str>,
|
||||
wallet_ledger_entry_type_override: Option<WalletLedgerEntryType>,
|
||||
transaction: &mut sqlx::Transaction<'_, MySql>,
|
||||
) -> anyhow::Result<WalletUpdateSummary> {
|
||||
|
||||
@@ -38,7 +39,7 @@ pub async fn add_durable_banked_balance_to_wallet(
|
||||
|
||||
let record = InsertWalletLedgerEntry {
|
||||
wallet_token,
|
||||
entry_type: WalletLedgerEntryType::CreditBanked,
|
||||
entry_type: wallet_ledger_entry_type_override.unwrap_or(WalletLedgerEntryType::CreditBanked),
|
||||
maybe_entity_ref: maybe_ledger_ref.map(|t| t.to_string()),
|
||||
|
||||
credits_delta: amount_to_add.to_i64().unwrap_or(0),
|
||||
@@ -54,7 +55,6 @@ pub async fn add_durable_banked_balance_to_wallet(
|
||||
|
||||
let wallet_ledger_entry_token = record.upsert_with_transaction(transaction).await?;
|
||||
|
||||
|
||||
Ok(WalletUpdateSummary {
|
||||
wallet_token: wallet.token,
|
||||
wallet_ledger_entry_token,
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
use anyhow::anyhow;
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
|
||||
pub struct WalletForModerationResult {
|
||||
pub token: WalletToken,
|
||||
pub wallet_namespace: PaymentsNamespace,
|
||||
pub owner_user_token: UserToken,
|
||||
pub banked_credits: u32,
|
||||
pub monthly_credits: u32,
|
||||
pub version: i32,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn get_wallet_for_moderation(
|
||||
wallet_token: &WalletToken,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<WalletForModerationResult>> {
|
||||
let result = sqlx::query_as!(
|
||||
WalletForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
w.token as `token: tokens::tokens::wallets::WalletToken`,
|
||||
w.wallet_namespace as `wallet_namespace: enums::common::payments_namespace::PaymentsNamespace`,
|
||||
w.owner_user_token as `owner_user_token: tokens::tokens::users::UserToken`,
|
||||
w.banked_credits as `banked_credits: u32`,
|
||||
w.monthly_credits as `monthly_credits: u32`,
|
||||
w.version,
|
||||
w.created_at as `created_at: DateTime<Utc>`,
|
||||
w.updated_at as `updated_at: DateTime<Utc>`
|
||||
FROM wallets as w
|
||||
WHERE w.token = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
wallet_token,
|
||||
)
|
||||
.fetch_one(mysql_pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(record) => Ok(Some(record)),
|
||||
Err(sqlx::Error::RowNotFound) => Ok(None),
|
||||
Err(err) => {
|
||||
warn!("get_wallet_for_moderation query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
use anyhow::anyhow;
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::tokens::wallets::WalletToken;
|
||||
|
||||
pub struct UserWalletForModerationResult {
|
||||
pub token: WalletToken,
|
||||
pub wallet_namespace: PaymentsNamespace,
|
||||
pub banked_credits: u32,
|
||||
pub monthly_credits: u32,
|
||||
pub version: i32,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn list_user_wallets_for_moderation(
|
||||
user_token: &UserToken,
|
||||
mysql_pool: &MySqlPool,
|
||||
) -> AnyhowResult<Vec<UserWalletForModerationResult>> {
|
||||
let results = sqlx::query_as!(
|
||||
UserWalletForModerationResult,
|
||||
r#"
|
||||
SELECT
|
||||
w.token as `token: tokens::tokens::wallets::WalletToken`,
|
||||
w.wallet_namespace as `wallet_namespace: enums::common::payments_namespace::PaymentsNamespace`,
|
||||
w.banked_credits as `banked_credits: u32`,
|
||||
w.monthly_credits as `monthly_credits: u32`,
|
||||
w.version,
|
||||
w.created_at as `created_at: DateTime<Utc>`,
|
||||
w.updated_at as `updated_at: DateTime<Utc>`
|
||||
FROM wallets as w
|
||||
WHERE w.owner_user_token = ?
|
||||
ORDER BY w.id DESC
|
||||
"#,
|
||||
user_token,
|
||||
)
|
||||
.fetch_all(mysql_pool)
|
||||
.await;
|
||||
|
||||
match results {
|
||||
Ok(records) => Ok(records),
|
||||
Err(err) => {
|
||||
warn!("list_user_wallets_for_moderation query error: {:?}", err);
|
||||
Err(anyhow!("query error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,6 @@ pub mod find_primary_wallet_token_for_owner;
|
||||
pub mod refill_monthly_credits_balance_on_wallet;
|
||||
pub mod refund;
|
||||
pub mod spend;
|
||||
pub mod get_wallet_for_moderation;
|
||||
pub mod list_user_wallets_for_moderation;
|
||||
pub mod wallet_update_summary;
|
||||
@@ -0,0 +1,41 @@
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
pub struct InsertWebReferralArgs<'a> {
|
||||
pub url: &'a str,
|
||||
pub maybe_domain: Option<&'a str>,
|
||||
pub utm_source: Option<&'a str>,
|
||||
pub utm_medium: Option<&'a str>,
|
||||
pub utm_campaign: Option<&'a str>,
|
||||
pub ip_address: &'a str,
|
||||
pub maybe_anonymous_visitor_token: Option<&'a str>,
|
||||
}
|
||||
|
||||
pub async fn insert_web_referral(
|
||||
mysql_pool: &Pool<MySql>,
|
||||
args: InsertWebReferralArgs<'_>,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO web_referrals (
|
||||
url,
|
||||
domain,
|
||||
utm_source,
|
||||
utm_medium,
|
||||
utm_campaign,
|
||||
ip_address,
|
||||
anonymous_visitor_token
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
args.url,
|
||||
args.maybe_domain,
|
||||
args.utm_source,
|
||||
args.utm_medium,
|
||||
args.utm_campaign,
|
||||
args.ip_address,
|
||||
args.maybe_anonymous_visitor_token,
|
||||
)
|
||||
.execute(mysql_pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod insert_web_referral;
|
||||
+12
-1
@@ -41,6 +41,10 @@ pub enum WalletLedgerEntryType {
|
||||
#[serde(rename = "refund_banked")]
|
||||
RefundBanked,
|
||||
|
||||
/// Support staff manually credit account
|
||||
#[serde(rename = "staff_add_banked")]
|
||||
StaffAddBanked,
|
||||
|
||||
// TODO: No clean way to do "mixed" refunds yet, and if we
|
||||
// refund close to the cutoff it might be unfair. Let's
|
||||
// just not do monthly refunds yet and instead credit our
|
||||
@@ -66,6 +70,7 @@ impl WalletLedgerEntryType {
|
||||
Self::DeductBanked => "deduct_banked",
|
||||
Self::DeductMonthly => "deduct_monthly",
|
||||
Self::RefundBanked => "refund_banked",
|
||||
Self::StaffAddBanked => "staff_add_banked",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +83,7 @@ impl WalletLedgerEntryType {
|
||||
"deduct_banked" => Ok(Self::DeductBanked),
|
||||
"deduct_monthly" => Ok(Self::DeductMonthly),
|
||||
"refund_banked" => Ok(Self::RefundBanked),
|
||||
"staff_add_banked" => Ok(Self::StaffAddBanked),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -93,6 +99,7 @@ impl WalletLedgerEntryType {
|
||||
Self::DeductBanked,
|
||||
Self::DeductMonthly,
|
||||
Self::RefundBanked,
|
||||
Self::StaffAddBanked,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -114,6 +121,7 @@ mod tests {
|
||||
assert_serialization(WalletLedgerEntryType::DeductBanked, "deduct_banked");
|
||||
assert_serialization(WalletLedgerEntryType::DeductMonthly, "deduct_monthly");
|
||||
assert_serialization(WalletLedgerEntryType::RefundBanked, "refund_banked");
|
||||
assert_serialization(WalletLedgerEntryType::StaffAddBanked, "staff_add_banked");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +137,7 @@ mod tests {
|
||||
assert_eq!(WalletLedgerEntryType::DeductBanked.to_str(), "deduct_banked");
|
||||
assert_eq!(WalletLedgerEntryType::DeductMonthly.to_str(), "deduct_monthly");
|
||||
assert_eq!(WalletLedgerEntryType::RefundBanked.to_str(), "refund_banked");
|
||||
assert_eq!(WalletLedgerEntryType::StaffAddBanked.to_str(), "staff_add_banked");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -140,6 +149,7 @@ mod tests {
|
||||
assert_eq!(WalletLedgerEntryType::from_str("deduct_banked").unwrap(), WalletLedgerEntryType::DeductBanked);
|
||||
assert_eq!(WalletLedgerEntryType::from_str("deduct_monthly").unwrap(), WalletLedgerEntryType::DeductMonthly);
|
||||
assert_eq!(WalletLedgerEntryType::from_str("refund_banked").unwrap(), WalletLedgerEntryType::RefundBanked);
|
||||
assert_eq!(WalletLedgerEntryType::from_str("staff_add_banked").unwrap(), WalletLedgerEntryType::StaffAddBanked);
|
||||
assert!(WalletLedgerEntryType::from_str("foo").is_err());
|
||||
}
|
||||
}
|
||||
@@ -150,7 +160,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = WalletLedgerEntryType::all_variants();
|
||||
assert_eq!(variants.len(), 7);
|
||||
assert_eq!(variants.len(), 8);
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::Create));
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::CreditBanked));
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::CreditMonthly));
|
||||
@@ -158,6 +168,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::DeductBanked));
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::DeductMonthly));
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::RefundBanked));
|
||||
assert_eq!(variants.pop_first(), Some(WalletLedgerEntryType::StaffAddBanked));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -38,9 +38,10 @@ pub async fn complete_credits_pack_purchase(
|
||||
info!("Adding {} credits to wallet: {}", credits_purchased, wallet_token.as_str());
|
||||
|
||||
let _result = add_durable_banked_balance_to_wallet(
|
||||
&wallet_token,
|
||||
credits_purchased,
|
||||
&wallet_token,
|
||||
credits_purchased,
|
||||
maybe_ledger_ref,
|
||||
None,
|
||||
transaction,
|
||||
).await?;
|
||||
|
||||
|
||||
-3
@@ -1,11 +1,8 @@
|
||||
use crate::billing_action_fulfillment::artcraft_billing_action::SubscriptionPaidEvent;
|
||||
use crate::billing_action_fulfillment::subscriptions::upsert_subscription_details::CrudType;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use log::{info, warn};
|
||||
use mysql_queries::queries::users::user_subscriptions::get_user_subscription_by_stripe_subscription_id::get_user_subscription_by_stripe_subscription_id_transactional;
|
||||
use mysql_queries::queries::users::user_subscriptions::upsert_user_subscription_by_stripe_id::UpsertUserSubscription;
|
||||
use mysql_queries::queries::users::user_subscriptions::upsert_user_subscription_with_invoice_paid_status_by_stripe_id::UpsertUserSubscriptionWithInvoicePaidStatus;
|
||||
use mysql_queries::queries::wallets::add_durable_banked_balance_to_wallet::add_durable_banked_balance_to_wallet;
|
||||
use mysql_queries::queries::wallets::create_new_artcraft_wallet_for_owner_user::create_new_artcraft_wallet_for_owner_user;
|
||||
use mysql_queries::queries::wallets::find_primary_wallet_token_for_owner::find_primary_wallet_token_for_owner_using_transaction;
|
||||
use mysql_queries::queries::wallets::refill_monthly_credits_balance_on_wallet::refill_monthly_credits_balance_on_wallet;
|
||||
|
||||
+10
@@ -85,6 +85,15 @@ pub async fn stripe_artcraft_create_subscription_checkout_with_user_signup_handl
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_referral_url = request.maybe_referral_url.clone()
|
||||
.or_else(|| {
|
||||
http_request.headers().get("referer")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.chars().take(255).collect::<String>());
|
||||
|
||||
let maybe_user_metadata = internal_user_lookup
|
||||
.lookup_user_from_http_request_and_mysql_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
@@ -101,6 +110,7 @@ pub async fn stripe_artcraft_create_subscription_checkout_with_user_signup_handl
|
||||
&price_id,
|
||||
&mut mysql_connection,
|
||||
&stripe_config,
|
||||
maybe_referral_url,
|
||||
).await?
|
||||
},
|
||||
Some(user_metadata) => {
|
||||
|
||||
+2
@@ -28,6 +28,7 @@ pub (super) async fn user_creation_case(
|
||||
price_id: &PriceId,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
stripe_config: &ArtcraftStripeConfigWithClient,
|
||||
maybe_referral_url: Option<String>,
|
||||
) -> Result<CreationPayload, CommonWebError> {
|
||||
|
||||
let mut transaction = mysql_connection.begin()
|
||||
@@ -82,6 +83,7 @@ pub (super) async fn user_creation_case(
|
||||
maybe_feature_flags: Some(&user_feature_flags),
|
||||
ip_address: &ip_address,
|
||||
maybe_source,
|
||||
maybe_referral_url: maybe_referral_url.clone(),
|
||||
},
|
||||
Transactor::for_transaction(&mut transaction),
|
||||
).await;
|
||||
|
||||
@@ -225,6 +225,65 @@ use tokens::tokens::user_bookmarks::*;
|
||||
use tokens::tokens::users::*;
|
||||
use tokens::tokens::zs_voice_datasets::*;
|
||||
|
||||
// Cost estimate
|
||||
use artcraft_api_defs::generate::cost_estimate::estimate_image_cost::*;
|
||||
use artcraft_api_defs::generate::cost_estimate::estimate_video_cost::*;
|
||||
// Image multi-function
|
||||
use artcraft_api_defs::generate::image::multi_function::nano_banana_multi_function_image_gen::*;
|
||||
use artcraft_api_defs::generate::image::multi_function::nano_banana_pro_multi_function_image_gen::*;
|
||||
use artcraft_api_defs::generate::image::multi_function::nano_banana_2_multi_function_image_gen::*;
|
||||
use artcraft_api_defs::generate::image::multi_function::gpt_image_1p5_multi_function_image_gen::*;
|
||||
use artcraft_api_defs::generate::image::multi_function::bytedance_seedream_v4_multi_function_image_gen::*;
|
||||
use artcraft_api_defs::generate::image::multi_function::bytedance_seedream_v4p5_multi_function_image_gen::*;
|
||||
use artcraft_api_defs::generate::image::multi_function::bytedance_seedream_5_lite_multi_function_image_gen::*;
|
||||
// Image edit (missing handlers)
|
||||
use artcraft_api_defs::generate::image::edit::flux_pro_kontext_max_edit_image::*;
|
||||
use artcraft_api_defs::generate::image::edit::gemini_25_flash_edit_image::*;
|
||||
use artcraft_api_defs::generate::image::edit::qwen_edit_image::*;
|
||||
use artcraft_api_defs::generate::image::edit::seededit_3_edit_image::*;
|
||||
// Image inpaint
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_dev_juggernaut_inpaint_image::*;
|
||||
use artcraft_api_defs::generate::image::inpaint::flux_pro_1_inpaint_image::*;
|
||||
// Video (missing handlers)
|
||||
use artcraft_api_defs::generate::video::generate_seedance_1_0_pro_image_to_video::*;
|
||||
use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::*;
|
||||
use artcraft_api_defs::generate::video::generate_veo_3_fast_image_to_video::*;
|
||||
// Video multi-function
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_2_5_turbo_multi_function_video_gen::*;
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_2_6_multi_function_video_gen::*;
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::*;
|
||||
use artcraft_api_defs::generate::video::multi_function::sora_2_multi_function_video_gen::*;
|
||||
use artcraft_api_defs::generate::video::multi_function::sora_2_pro_multi_function_video_gen::*;
|
||||
use artcraft_api_defs::generate::video::multi_function::veo_3p1_multi_function_video_gen::*;
|
||||
use artcraft_api_defs::generate::video::multi_function::veo_3p1_fast_multi_function_video_gen::*;
|
||||
// Object multi-function
|
||||
use artcraft_api_defs::generate::object::multi_function::hunyuan3d_v3_multi_function_object_gen::*;
|
||||
// Analytics, credits, subscriptions, media files
|
||||
use artcraft_api_defs::analytics::log_active_user::*;
|
||||
use artcraft_api_defs::credits::get_session_credits::*;
|
||||
use artcraft_api_defs::subscriptions::get_session_subscription::*;
|
||||
use artcraft_api_defs::media_file::list_batch_generated_media_files::*;
|
||||
// Handler modules with locally-defined types
|
||||
use crate::http_server::endpoints::moderation::info::moderator_token_info_handler::*;
|
||||
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::wallet_ledger_entries::moderator_get_wallet_ledger_entry::*;
|
||||
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_get_wallet::*;
|
||||
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::webhooks::fal_webhook_handler::*;
|
||||
use crate::http_server::endpoints::image_studio::update_gpt_image_job_status_handler::*;
|
||||
use crate::http_server::endpoints::voice_conversion::enqueue_seed_vc_inference_handler::*;
|
||||
use crate::http_server::endpoints::credits::get_session_credits_handler::*;
|
||||
use crate::http_server::endpoints::subscriptions::get_session_subscription_handler::*;
|
||||
use crate::http_server::endpoints::analytics::log_app_active_user_handler::*;
|
||||
use crate::http_server::endpoints::analytics::log_app_active_user_json_handler::*;
|
||||
use crate::http_server::endpoints::media_files::list::list_batch_generated_redux_media_files_handler::*;
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
paths(
|
||||
@@ -341,6 +400,68 @@ use tokens::tokens::zs_voice_datasets::*;
|
||||
crate::http_server::endpoints::weights::search::search_model_weights_http_post_handler::search_model_weights_http_post_handler,
|
||||
crate::http_server::endpoints::weights::update::set_model_weight_cover_image_handler::set_model_weight_cover_image_handler,
|
||||
crate::http_server::endpoints::weights::update::update_weight_handler::update_weight_handler,
|
||||
// Cost Estimate
|
||||
crate::http_server::endpoints::generate::cost_estimate::image::estimate_image_cost_handler::estimate_image_cost_handler,
|
||||
crate::http_server::endpoints::generate::cost_estimate::video::estimate_video_cost_handler::estimate_video_cost_handler,
|
||||
// Generate Images (Multi-Function)
|
||||
crate::http_server::endpoints::generate::image::multi_function::nano_banana_multi_function_image_gen_handler::nano_banana_multi_function_image_gen_handler,
|
||||
crate::http_server::endpoints::generate::image::multi_function::nano_banana_pro_multi_function_image_gen_handler::nano_banana_pro_multi_function_image_gen_handler,
|
||||
crate::http_server::endpoints::generate::image::multi_function::nano_banana_2_multi_function_image_gen_handler::nano_banana_2_multi_function_image_gen_handler,
|
||||
crate::http_server::endpoints::generate::image::multi_function::gpt_image_1p5_multi_function_image_gen_handler::gpt_image_1p5_multi_function_image_gen_handler,
|
||||
crate::http_server::endpoints::generate::image::multi_function::bytedance_seedream_v4_multi_function_image_gen_handler::bytedance_seedream_v4_multi_function_image_gen_handler,
|
||||
crate::http_server::endpoints::generate::image::multi_function::bytedance_seedream_v4p5_multi_function_image_gen_handler::bytedance_seedream_v4p5_multi_function_image_gen_handler,
|
||||
crate::http_server::endpoints::generate::image::multi_function::bytedance_seedream_5_lite_multi_function_image_gen_handler::bytedance_seedream_5_lite_multi_function_image_gen_handler,
|
||||
// Generate Images (Edit)
|
||||
crate::http_server::endpoints::generate::image::edit::flux_pro_kontext_max_edit_image_handler::flux_pro_kontext_max_edit_image_handler,
|
||||
crate::http_server::endpoints::generate::image::edit::gemini_25_flash_edit_image_handler::gemini_25_flash_edit_image_handler,
|
||||
crate::http_server::endpoints::generate::image::edit::qwen_edit_image_handler::qwen_edit_image_handler,
|
||||
crate::http_server::endpoints::generate::image::edit::seededit_3_edit_image_handler::seededit_3_edit_image_handler,
|
||||
// Generate Images (Inpaint)
|
||||
crate::http_server::endpoints::generate::image::inpaint::flux_dev_juggernaut_inpaint_handler::flux_dev_juggernaut_inpaint_image_handler,
|
||||
crate::http_server::endpoints::generate::image::inpaint::flux_pro_1_inpaint_handler::flux_pro_1_inpaint_image_handler,
|
||||
// Generate Videos
|
||||
crate::http_server::endpoints::generate::video::generate_seedance_1_0_pro_image_to_video_handler::generate_seedance_1_0_pro_image_to_video_handler,
|
||||
crate::http_server::endpoints::generate::video::generate_veo_3_image_to_video_handler::generate_veo_3_image_to_video_handler,
|
||||
crate::http_server::endpoints::generate::video::generate_veo_3_fast_image_to_video_handler::generate_veo_3_fast_image_to_video_handler,
|
||||
// Generate Video (Multi-Function)
|
||||
crate::http_server::endpoints::generate::video::multi_function::kling_2p5_turbo_pro_multi_function_video_gen_handler::kling_2p5_turbo_pro_multi_function_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::multi_function::kling_2p6_pro_multi_function_video_gen_handler::kling_2p6_pro_multi_function_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::multi_function::seedance_2p0_multi_function_video_gen_handler::seedance_2p0_multi_function_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::multi_function::sora_2_multi_function_video_gen_handler::sora_2_multi_function_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::multi_function::sora_2_pro_multi_function_video_gen_handler::sora_2_pro_multi_function_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::multi_function::veo_3p1_multi_function_video_gen_handler::veo_3p1_multi_function_video_gen_handler,
|
||||
crate::http_server::endpoints::generate::video::multi_function::veo_3p1_fast_multi_function_video_gen_handler::veo_3p1_fast_multi_function_video_gen_handler,
|
||||
// Generate Objects (Multi-Function)
|
||||
crate::http_server::endpoints::generate::object::multi_function::generate_hunyuan3d_v3_multi_function_object_handler::generate_hunyuan3d_v3_multi_function_object_handler,
|
||||
// Media Files
|
||||
crate::http_server::endpoints::media_files::list::list_batch_generated_redux_media_files_handler::list_batch_generated_redux_media_files_handler,
|
||||
// Analytics
|
||||
crate::http_server::endpoints::analytics::log_app_active_user_handler::log_app_active_user_handler,
|
||||
crate::http_server::endpoints::analytics::log_app_active_user_json_handler::log_app_active_user_json_handler,
|
||||
// Voice Conversion
|
||||
crate::http_server::endpoints::voice_conversion::enqueue_seed_vc_inference_handler::enqueue_infer_seed_vc_handler,
|
||||
// TTS
|
||||
crate::http_server::endpoints::tts::enqueue_infer_f5_tts_handler::enqueue_infer_f5_tts_handler::enqueue_infer_f5_tts_handler,
|
||||
// Moderation
|
||||
crate::http_server::endpoints::moderation::info::moderator_token_info_handler::moderator_get_token_info_handler,
|
||||
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::wallet_ledger_entries::moderator_get_wallet_ledger_entry_handler::moderator_get_wallet_ledger_entry_handler,
|
||||
crate::http_server::endpoints::moderation::wallets::list_user_wallets_handler::list_user_wallets_handler,
|
||||
crate::http_server::endpoints::moderation::wallets::moderator_add_banked_balance_to_wallet_handler::moderator_add_banked_balance_to_wallet_handler,
|
||||
crate::http_server::endpoints::moderation::wallets::moderator_get_wallet_handler::moderator_get_wallet_handler,
|
||||
// Credits
|
||||
crate::http_server::endpoints::credits::get_session_credits_handler::get_session_credits_handler,
|
||||
// Subscriptions
|
||||
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,
|
||||
// Webhooks
|
||||
crate::http_server::endpoints::webhooks::fal_webhook_handler::fal_webhook_handler,
|
||||
// Image Studio
|
||||
crate::http_server::endpoints::image_studio::update_gpt_image_job_status_handler::update_gpt_image_job_status_handler,
|
||||
),
|
||||
components(schemas(
|
||||
// Tokens
|
||||
@@ -510,6 +631,30 @@ use tokens::tokens::zs_voice_datasets::*;
|
||||
EditUserFeatureFlagsError,
|
||||
EditUserFeatureFlagsOption,
|
||||
EditUserFeatureFlagsRequest,
|
||||
ModeratorUserLookupRequest,
|
||||
ModeratorUserLookupSuccessResponse,
|
||||
ModeratorUserLookupUserDetails,
|
||||
ModeratorUserLookupByStripeCustomerIdRequest,
|
||||
ModeratorUserLookupByStripeCustomerIdResponse,
|
||||
ModeratorUserLookupByStripeCustomerIdEntry,
|
||||
ListUserJobsPathInfo,
|
||||
ListUserJobsResponse,
|
||||
ListUserJobsEntry,
|
||||
ListUserWalletsPathInfo,
|
||||
ListUserWalletsResponse,
|
||||
ListUserWalletsEntry,
|
||||
ListWalletLedgerEntriesByWalletPathInfo,
|
||||
ListWalletLedgerEntriesByWalletResponse,
|
||||
ListWalletLedgerEntriesByWalletEntry,
|
||||
ModeratorAddBankedBalanceToWalletPathInfo,
|
||||
ModeratorAddBankedBalanceToWalletRequest,
|
||||
ModeratorAddBankedBalanceToWalletResponse,
|
||||
ModeratorGetWalletPathInfo,
|
||||
ModeratorGetWalletResponse,
|
||||
ModeratorGetWalletDetails,
|
||||
ModeratorGetWalletLedgerEntryPathInfo,
|
||||
ModeratorGetWalletLedgerEntryResponse,
|
||||
ModeratorGetWalletLedgerEntryDetails,
|
||||
GptImage1EditImageRequest,
|
||||
GptImage1EditImageImageSize,
|
||||
GptImage1EditImageNumImages,
|
||||
@@ -797,6 +942,115 @@ use tokens::tokens::zs_voice_datasets::*;
|
||||
WriteEngineAssetMediaSuccessResponse,
|
||||
WriteSceneFileMediaSuccessResponse,
|
||||
ZsDatasetRecord,
|
||||
|
||||
// Cost Estimate types
|
||||
EstimateImageCostRequest,
|
||||
EstimateImageCostResponse,
|
||||
EstimateImageCostError,
|
||||
EstimateImageCostErrorType,
|
||||
EstimateVideoCostRequest,
|
||||
EstimateVideoCostResponse,
|
||||
EstimateVideoCostError,
|
||||
EstimateVideoCostErrorType,
|
||||
|
||||
// Image Multi-Function types
|
||||
NanoBananaMultiFunctionImageGenRequest,
|
||||
NanoBananaMultiFunctionImageGenResponse,
|
||||
NanoBananaMultiFunctionImageGenAspectRatio,
|
||||
NanoBananaMultiFunctionImageGenNumImages,
|
||||
NanoBananaProMultiFunctionImageGenRequest,
|
||||
NanoBananaProMultiFunctionImageGenResponse,
|
||||
NanoBananaProMultiFunctionImageGenAspectRatio,
|
||||
NanoBananaProMultiFunctionImageGenNumImages,
|
||||
NanoBananaProMultiFunctionImageGenImageResolution,
|
||||
NanaBanana2MultiFunctionImageGenRequest,
|
||||
NanaBanana2MultiFunctionImageGenResponse,
|
||||
NanaBanana2MultiFunctionImageGenAspectRatio,
|
||||
NanaBanana2MultiFunctionImageGenNumImages,
|
||||
NanaBanana2MultiFunctionImageGenImageResolution,
|
||||
GptImage1p5MultiFunctionImageGenRequest,
|
||||
GptImage1p5MultiFunctionImageGenResponse,
|
||||
GptImage1p5MultiFunctionImageGenBackground,
|
||||
GptImage1p5MultiFunctionImageGenInputFidelity,
|
||||
GptImage1p5MultiFunctionImageGenNumImages,
|
||||
GptImage1p5MultiFunctionImageGenQuality,
|
||||
GptImage1p5MultiFunctionImageGenSize,
|
||||
BytedanceSeedreamV4MultiFunctionImageGenRequest,
|
||||
BytedanceSeedreamV4MultiFunctionImageGenResponse,
|
||||
BytedanceSeedreamV4MultiFunctionImageGenImageSize,
|
||||
BytedanceSeedreamV4MultiFunctionImageGenNumImages,
|
||||
BytedanceSeedreamV4p5MultiFunctionImageGenRequest,
|
||||
BytedanceSeedreamV4p5MultiFunctionImageGenResponse,
|
||||
BytedanceSeedreamV4p5MultiFunctionImageGenImageSize,
|
||||
BytedanceSeedreamV4p5MultiFunctionImageGenNumImages,
|
||||
BytedanceSeedream5LiteMultiFunctionImageGenRequest,
|
||||
BytedanceSeedream5LiteMultiFunctionImageGenResponse,
|
||||
BytedanceSeedream5LiteMultiFunctionImageGenImageSize,
|
||||
BytedanceSeedream5LiteMultiFunctionImageGenNumImages,
|
||||
|
||||
// Image Edit types
|
||||
FluxProKontextMaxEditImageRequest,
|
||||
FluxProKontextMaxEditImageResponse,
|
||||
FluxProKontextMaxEditImageNumImages,
|
||||
Gemini25FlashEditImageRequest,
|
||||
Gemini25FlashEditImageResponse,
|
||||
Gemini25FlashEditImageNumImages,
|
||||
QwenEditImageRequest,
|
||||
QwenEditImageResponse,
|
||||
QwenEditImageAcceleration,
|
||||
QwenEditImageNumImages,
|
||||
SeedEdit3EditImageRequest,
|
||||
SeedEdit3EditImageResponse,
|
||||
|
||||
// Image Inpaint types
|
||||
FluxDevJuggernautInpaintImageRequest,
|
||||
FluxDevJuggernautInpaintImageResponse,
|
||||
FluxDevJuggernautInpaintImageNumImages,
|
||||
FluxPro1InpaintImageRequest,
|
||||
FluxPro1InpaintImageResponse,
|
||||
FluxPro1InpaintImageNumImages,
|
||||
|
||||
// Video types (missing handlers)
|
||||
GenerateSeedance10ProImageToVideoRequest,
|
||||
GenerateSeedance10ProImageToVideoResponse,
|
||||
GenerateVeo3ImageToVideoRequest,
|
||||
GenerateVeo3ImageToVideoResponse,
|
||||
GenerateVeo3FastImageToVideoRequest,
|
||||
GenerateVeo3FastImageToVideoResponse,
|
||||
|
||||
// Video Multi-Function types
|
||||
Kling2p5TurboProMultiFunctionVideoGenRequest,
|
||||
Kling2p5TurboProMultiFunctionVideoGenResponse,
|
||||
Kling2p6ProMultiFunctionVideoGenRequest,
|
||||
Kling2p6ProMultiFunctionVideoGenResponse,
|
||||
Seedance2p0MultiFunctionVideoGenRequest,
|
||||
Seedance2p0MultiFunctionVideoGenResponse,
|
||||
Sora2MultiFunctionVideoGenRequest,
|
||||
Sora2MultiFunctionVideoGenResponse,
|
||||
Sora2ProMultiFunctionVideoGenRequest,
|
||||
Sora2ProMultiFunctionVideoGenResponse,
|
||||
Veo3p1MultiFunctionVideoGenRequest,
|
||||
Veo3p1MultiFunctionVideoGenResponse,
|
||||
Veo3p1FastMultiFunctionVideoGenRequest,
|
||||
Veo3p1FastMultiFunctionVideoGenResponse,
|
||||
|
||||
// Object Multi-Function types
|
||||
Hunyuan3dV3MultiFunctionObjectGenRequest,
|
||||
Hunyuan3dV3MultiFunctionObjectGenResponse,
|
||||
|
||||
// Other types
|
||||
LogAppActiveUserRequest,
|
||||
LogAppActiveUserResponse,
|
||||
GetSessionCreditsPathInfo,
|
||||
GetSessionCreditsResponse,
|
||||
GetSessionSubscriptionPathInfo,
|
||||
GetSessionSubscriptionResponse,
|
||||
ListBatchGeneratedReduxMediaFilesPathInfo,
|
||||
ListBatchGeneratedReduxMediaFilesSuccessResponse,
|
||||
|
||||
// Web Referrals
|
||||
LogWebReferralRequest,
|
||||
LogWebReferralResponse,
|
||||
))
|
||||
)]
|
||||
pub struct ApiDoc;
|
||||
|
||||
+6
-1
@@ -31,7 +31,12 @@ impl AvtCookieManager {
|
||||
}
|
||||
|
||||
pub fn make_new_cookie(&self) -> AnyhowResult<Cookie> {
|
||||
let payload = AvtCookiePayload::new();
|
||||
let token = AnonymousVisitorTrackingToken::generate();
|
||||
self.make_new_cookie_with_apriori_token(&token)
|
||||
}
|
||||
|
||||
pub fn make_new_cookie_with_apriori_token(&self, token: &AnonymousVisitorTrackingToken) -> AnyhowResult<Cookie> {
|
||||
let payload = AvtCookiePayload::from_token(token.clone());
|
||||
let claims = payload.to_map();
|
||||
let jwt_string = self.jwt_signer.claims_to_jwt(&claims)?;
|
||||
|
||||
|
||||
+7
@@ -36,6 +36,13 @@ impl AvtCookiePayload {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_token(avt_token: AnonymousVisitorTrackingToken) -> Self {
|
||||
AvtCookiePayload {
|
||||
avt_token,
|
||||
cookie_version: COOKIE_VERSION,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_map(map: BTreeMap<String, String>) -> Result<Self, InternalError> {
|
||||
let avt_token = map.get("avt_token")
|
||||
.ok_or_else(|| InternalError::VisitorCookieMissingField("avt_token"))?;
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ pub async fn create_beta_keys_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, CreateBetaKeysError>
|
||||
{
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::Implicit)
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => CreateBetaKeysError::ServerError,
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ pub async fn edit_beta_key_distributed_flag_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, EditBetaKeyDistributedFlagError>
|
||||
{
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::Implicit)
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => EditBetaKeyDistributedFlagError::ServerError,
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ pub async fn edit_beta_key_note_handler(
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, EditBetaKeyNoteError>
|
||||
{
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::Implicit)
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => EditBetaKeyNoteError::ServerError,
|
||||
|
||||
@@ -27,5 +27,6 @@ pub mod users;
|
||||
pub mod voice_clone_requests;
|
||||
pub mod voice_conversion;
|
||||
pub mod voice_designer;
|
||||
pub mod web_referrals;
|
||||
pub mod webhooks;
|
||||
pub mod weights;
|
||||
|
||||
+2
-1
@@ -68,6 +68,7 @@ impl fmt::Display for ModeratorTokenInfoError {
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/token_info/{token}",
|
||||
responses(
|
||||
(status = 200, description = "Found", body = ModeratorTokenInfoResponse),
|
||||
@@ -84,7 +85,7 @@ pub async fn moderator_get_token_info_handler(
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<HttpResponse, ModeratorTokenInfoError> {
|
||||
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::Implicit)
|
||||
let user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
RequireModeratorError::ServerError => ModeratorTokenInfoError::ServerError,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod get_tts_inference_queue_count;
|
||||
pub mod get_w2l_inference_queue_count;
|
||||
pub mod kill_tts_inference_jobs;
|
||||
pub mod kill_tts_inference_jobs;
|
||||
pub mod user;
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::jobs::user::list_user_jobs::{
|
||||
ListUserJobsEntry,
|
||||
ListUserJobsPathInfo,
|
||||
ListUserJobsResponse,
|
||||
};
|
||||
use mysql_queries::queries::generic_inference::web::list_user_jobs_for_moderation::list_user_jobs_for_moderation;
|
||||
|
||||
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 jobs for a user (moderation)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/jobs/user/{user_token}/list",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ListUserJobsResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
params(
|
||||
("user_token" = UserToken, Path, description = "User token to look up jobs for"),
|
||||
)
|
||||
)]
|
||||
pub async fn list_user_jobs_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ListUserJobsPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListUserJobsResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let results = list_user_jobs_for_moderation(&path.user_token, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("list_user_jobs error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let jobs = results.into_iter().map(|row| ListUserJobsEntry {
|
||||
job_status: row.job_status,
|
||||
job_failure_reason: row.job_failure_reason,
|
||||
credits_delta: row.credits_delta,
|
||||
maybe_linked_refund_ledger_token: row.maybe_linked_refund_ledger_token,
|
||||
on_success_result_media_token: row.on_success_result_media_token,
|
||||
job_token: row.job_token,
|
||||
wallet_ledger_entry_token: row.wallet_ledger_entry_token,
|
||||
wallet_ledger_entry_type: row.wallet_ledger_entry_type,
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ListUserJobsResponse {
|
||||
success: true,
|
||||
jobs,
|
||||
}))
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
pub mod list_user_jobs_handler;
|
||||
@@ -1,5 +1,8 @@
|
||||
pub mod info;
|
||||
pub mod ip_bans;
|
||||
pub mod jobs;
|
||||
pub mod user;
|
||||
pub mod user_bans;
|
||||
pub mod user_feature_flags;
|
||||
pub mod user_feature_flags;
|
||||
pub mod wallet_ledger_entries;
|
||||
pub mod wallets;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod moderator_user_lookup_by_stripe_customer_id_handler;
|
||||
pub mod moderator_user_lookup_handler;
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::user::user_lookup_by_stripe_customer_id::{
|
||||
ModeratorUserLookupByStripeCustomerIdEntry,
|
||||
ModeratorUserLookupByStripeCustomerIdRequest,
|
||||
ModeratorUserLookupByStripeCustomerIdResponse,
|
||||
};
|
||||
use mysql_queries::queries::users::user_subscriptions::lookup_users_by_stripe_customer_id::lookup_users_by_stripe_customer_id;
|
||||
|
||||
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;
|
||||
|
||||
/// Moderator User Lookup by Stripe Customer ID
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/users/lookup_by_stripe_customer_id",
|
||||
request_body = ModeratorUserLookupByStripeCustomerIdRequest,
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModeratorUserLookupByStripeCustomerIdResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn moderator_user_lookup_by_stripe_customer_id_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<ModeratorUserLookupByStripeCustomerIdRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorUserLookupByStripeCustomerIdResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let stripe_customer_id = request.stripe_customer_id.trim();
|
||||
|
||||
if stripe_customer_id.is_empty() {
|
||||
return Ok(Json(ModeratorUserLookupByStripeCustomerIdResponse {
|
||||
success: true,
|
||||
users: vec![],
|
||||
}));
|
||||
}
|
||||
|
||||
let results = lookup_users_by_stripe_customer_id(stripe_customer_id, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_user_lookup_by_stripe_customer_id error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let users = results.into_iter().map(|row| ModeratorUserLookupByStripeCustomerIdEntry {
|
||||
subscription_namespace: row.subscription_namespace,
|
||||
maybe_stripe_subscription_id: row.maybe_stripe_subscription_id,
|
||||
token: row.user_token,
|
||||
email_address: row.email_address,
|
||||
username: row.username,
|
||||
display_name: row.display_name,
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ModeratorUserLookupByStripeCustomerIdResponse {
|
||||
success: true,
|
||||
users,
|
||||
}))
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::user::user_lookup::{
|
||||
ModeratorUserLookupRequest,
|
||||
ModeratorUserLookupSuccessResponse,
|
||||
ModeratorUserLookupUserDetails,
|
||||
};
|
||||
use mysql_queries::queries::users::user::get::lookup_user_for_moderation::{
|
||||
lookup_user_for_moderation_by_email,
|
||||
lookup_user_for_moderation_by_token,
|
||||
lookup_user_for_moderation_by_username,
|
||||
};
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
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;
|
||||
|
||||
const LEGACY_USER_TOKEN_PREFIX: &str = "U:";
|
||||
|
||||
/// Moderator User Lookup
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/users/lookup",
|
||||
request_body = ModeratorUserLookupRequest,
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModeratorUserLookupSuccessResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn moderator_user_lookup_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<ModeratorUserLookupRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorUserLookupSuccessResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let search = request.search.trim();
|
||||
|
||||
if search.is_empty() {
|
||||
return Ok(Json(ModeratorUserLookupSuccessResponse {
|
||||
success: true,
|
||||
maybe_user: None,
|
||||
}));
|
||||
}
|
||||
|
||||
let maybe_result = if search.starts_with(UserToken::token_prefix()) || search.starts_with(LEGACY_USER_TOKEN_PREFIX) {
|
||||
lookup_user_for_moderation_by_token(search, &server_state.mysql_pool).await
|
||||
} else if search.contains('@') {
|
||||
lookup_user_for_moderation_by_email(search, &server_state.mysql_pool).await
|
||||
} else {
|
||||
lookup_user_for_moderation_by_username(search, &server_state.mysql_pool).await
|
||||
};
|
||||
|
||||
let maybe_user = maybe_result
|
||||
.map_err(|err| {
|
||||
warn!("moderator_user_lookup error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_user_details = maybe_user.map(|user| ModeratorUserLookupUserDetails {
|
||||
token: user.user_token,
|
||||
username: user.username,
|
||||
display_name: user.display_name,
|
||||
username_is_generated: user.username_is_generated,
|
||||
is_temporary: user.is_temporary,
|
||||
username_is_not_customized: user.username_is_not_customized,
|
||||
email_address: user.email_address,
|
||||
email_confirmed: user.email_confirmed,
|
||||
email_is_synthetic: user.email_is_synthetic,
|
||||
is_without_password: user.is_without_password,
|
||||
ip_address_creation: user.ip_address_creation,
|
||||
ip_address_last_login: user.ip_address_last_login,
|
||||
maybe_avatar_media_file_token: user.maybe_avatar_media_file_token,
|
||||
email_gravatar_hash: user.email_gravatar_hash,
|
||||
is_banned: user.is_banned,
|
||||
created_at: user.created_at,
|
||||
updated_at: user.updated_at,
|
||||
});
|
||||
|
||||
Ok(Json(ModeratorUserLookupSuccessResponse {
|
||||
success: true,
|
||||
maybe_user: maybe_user_details,
|
||||
}))
|
||||
}
|
||||
+68
@@ -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<ListWalletLedgerEntriesByWalletPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListWalletLedgerEntriesByWalletResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.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,
|
||||
}))
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
pub mod list_wallet_ledger_entries_by_wallet_handler;
|
||||
pub mod moderator_get_wallet_ledger_entry_handler;
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
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::moderator_get_wallet_ledger_entry::{
|
||||
ModeratorGetWalletLedgerEntryDetails,
|
||||
ModeratorGetWalletLedgerEntryPathInfo,
|
||||
ModeratorGetWalletLedgerEntryResponse,
|
||||
};
|
||||
use mysql_queries::queries::wallet_ledger_entries::get_wallet_ledger_entry_for_moderation::get_wallet_ledger_entry_for_moderation;
|
||||
|
||||
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;
|
||||
|
||||
/// Get a single wallet ledger entry by token (moderation)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/wallet_ledger_entry/{wallet_ledger_entry_token}",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModeratorGetWalletLedgerEntryResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
params(
|
||||
("wallet_ledger_entry_token" = WalletLedgerEntryToken, Path, description = "Wallet ledger entry token to look up"),
|
||||
)
|
||||
)]
|
||||
pub async fn moderator_get_wallet_ledger_entry_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ModeratorGetWalletLedgerEntryPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorGetWalletLedgerEntryResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let maybe_entry = get_wallet_ledger_entry_for_moderation(&path.wallet_ledger_entry_token, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_get_wallet_ledger_entry error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_entry_details = maybe_entry.map(|entry| ModeratorGetWalletLedgerEntryDetails {
|
||||
token: entry.token,
|
||||
entry_type: entry.entry_type,
|
||||
maybe_entity_ref: entry.maybe_entity_ref,
|
||||
credits_delta: entry.credits_delta,
|
||||
banked_credits_before: entry.banked_credits_before,
|
||||
banked_credits_after: entry.banked_credits_after,
|
||||
monthly_credits_before: entry.monthly_credits_before,
|
||||
monthly_credits_after: entry.monthly_credits_after,
|
||||
is_refunded: entry.is_refunded,
|
||||
maybe_linked_refund_ledger_token: entry.maybe_linked_refund_ledger_token,
|
||||
created_at: entry.created_at,
|
||||
});
|
||||
|
||||
Ok(Json(ModeratorGetWalletLedgerEntryResponse {
|
||||
success: true,
|
||||
maybe_entry: maybe_entry_details,
|
||||
}))
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::wallets::list_user_wallets::{
|
||||
ListUserWalletsEntry,
|
||||
ListUserWalletsPathInfo,
|
||||
ListUserWalletsResponse,
|
||||
};
|
||||
use mysql_queries::queries::wallets::list_user_wallets_for_moderation::list_user_wallets_for_moderation;
|
||||
|
||||
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 wallets for a user (moderation)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/wallets/user/{user_token}/list",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ListUserWalletsResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
params(
|
||||
("user_token" = UserToken, Path, description = "User token to look up wallets for"),
|
||||
)
|
||||
)]
|
||||
pub async fn list_user_wallets_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ListUserWalletsPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ListUserWalletsResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let results = list_user_wallets_for_moderation(&path.user_token, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("list_user_wallets error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let wallets = results.into_iter().map(|row| ListUserWalletsEntry {
|
||||
token: row.token,
|
||||
wallet_namespace: row.wallet_namespace,
|
||||
banked_credits: row.banked_credits,
|
||||
monthly_credits: row.monthly_credits,
|
||||
version: row.version,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at,
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ListUserWalletsResponse {
|
||||
success: true,
|
||||
wallets,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod list_user_wallets_handler;
|
||||
pub mod moderator_add_banked_balance_to_wallet_handler;
|
||||
pub mod moderator_get_wallet_handler;
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::wallets::moderator_add_banked_balance_to_wallet::{
|
||||
ModeratorAddBankedBalanceToWalletPathInfo,
|
||||
ModeratorAddBankedBalanceToWalletRequest,
|
||||
ModeratorAddBankedBalanceToWalletResponse,
|
||||
};
|
||||
use enums::by_table::wallet_ledger_entries::wallet_ledger_entry_type::WalletLedgerEntryType;
|
||||
use mysql_queries::queries::wallets::add_durable_banked_balance_to_wallet::add_durable_banked_balance_to_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;
|
||||
|
||||
/// Add banked credits to a wallet (moderation)
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/wallet/{wallet_token}/add_banked_balance",
|
||||
request_body = ModeratorAddBankedBalanceToWalletRequest,
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModeratorAddBankedBalanceToWalletResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
params(
|
||||
("wallet_token" = WalletToken, Path, description = "Wallet token to add credits to"),
|
||||
)
|
||||
)]
|
||||
pub async fn moderator_add_banked_balance_to_wallet_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ModeratorAddBankedBalanceToWalletPathInfo>,
|
||||
request: Json<ModeratorAddBankedBalanceToWalletRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorAddBankedBalanceToWalletResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let mut transaction = server_state.mysql_pool.begin()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_add_banked_balance_to_wallet transaction begin error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let _result = add_durable_banked_balance_to_wallet(
|
||||
&path.wallet_token,
|
||||
request.credits as u64,
|
||||
None,
|
||||
Some(WalletLedgerEntryType::StaffAddBanked),
|
||||
&mut transaction,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_add_banked_balance_to_wallet error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
transaction.commit()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_add_banked_balance_to_wallet commit error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
Ok(Json(ModeratorAddBankedBalanceToWalletResponse {
|
||||
success: true,
|
||||
}))
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use log::warn;
|
||||
|
||||
use artcraft_api_defs::moderation::wallets::moderator_get_wallet::{
|
||||
ModeratorGetWalletDetails,
|
||||
ModeratorGetWalletPathInfo,
|
||||
ModeratorGetWalletResponse,
|
||||
};
|
||||
use mysql_queries::queries::wallets::get_wallet_for_moderation::get_wallet_for_moderation;
|
||||
|
||||
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;
|
||||
|
||||
/// Get a single wallet by token (moderation)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Moderation",
|
||||
path = "/v1/moderation/wallet/{wallet_token}",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ModeratorGetWalletResponse),
|
||||
(status = 401, description = "Unauthorized"),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
params(
|
||||
("wallet_token" = WalletToken, Path, description = "Wallet token to look up"),
|
||||
)
|
||||
)]
|
||||
pub async fn moderator_get_wallet_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ModeratorGetWalletPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<Json<ModeratorGetWalletResponse>, CommonWebError> {
|
||||
|
||||
let _user_session = require_moderator(&http_request, &server_state, UseDatabase::GrabNewConnection)
|
||||
.await
|
||||
.map_err(|_| CommonWebError::NotAuthorized)?;
|
||||
|
||||
let maybe_wallet = get_wallet_for_moderation(&path.wallet_token, &server_state.mysql_pool)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("moderator_get_wallet error: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_wallet_details = maybe_wallet.map(|wallet| ModeratorGetWalletDetails {
|
||||
token: wallet.token,
|
||||
wallet_namespace: wallet.wallet_namespace,
|
||||
owner_user_token: wallet.owner_user_token,
|
||||
banked_credits: wallet.banked_credits,
|
||||
monthly_credits: wallet.monthly_credits,
|
||||
version: wallet.version,
|
||||
created_at: wallet.created_at,
|
||||
updated_at: wallet.updated_at,
|
||||
});
|
||||
|
||||
Ok(Json(ModeratorGetWalletResponse {
|
||||
success: true,
|
||||
maybe_wallet: maybe_wallet_details,
|
||||
}))
|
||||
}
|
||||
+15
@@ -41,6 +41,11 @@ pub struct CreateAccountRequest {
|
||||
/// Optional: Source of the signup, e.g. "artcraft", "fakeyou", "storyteller", etc.
|
||||
/// If not provided, we try to infer it from the Origin header instead.
|
||||
pub signup_source: Option<UserSignupSource>,
|
||||
|
||||
/// Optional: The referral URL the user arrived from when signing up.
|
||||
/// The browser can send `document.referrer` to the backend so we know how people are finding us.
|
||||
/// If the browser doesn't send this parameter, we'll try the `referer` header.
|
||||
pub maybe_referral_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(ToSchema, Serialize)]
|
||||
@@ -195,6 +200,15 @@ pub async fn create_account_handler(
|
||||
maybe_source = get_request_signup_source_enum(&http_request);
|
||||
}
|
||||
|
||||
let maybe_referral_url = request.maybe_referral_url.clone()
|
||||
.or_else(|| {
|
||||
http_request.headers().get("referer")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.chars().take(255).collect::<String>());
|
||||
|
||||
let create_account_result = create_account_from_email_and_password(
|
||||
&mysql_pool,
|
||||
CreateAccountFromEmailPasswordArgs {
|
||||
@@ -205,6 +219,7 @@ pub async fn create_account_handler(
|
||||
password_hash: &password_hash,
|
||||
ip_address: &ip_address,
|
||||
maybe_source,
|
||||
maybe_referral_url,
|
||||
maybe_user_token: None, // NB: This parameter is for internal testing only
|
||||
}
|
||||
).await;
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use log::{info, warn};
|
||||
use url::Url;
|
||||
|
||||
use artcraft_api_defs::web_referrals::log_web_referral::{LogWebReferralRequest, LogWebReferralResponse};
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use mysql_queries::queries::web_referrals::insert_web_referral::{insert_web_referral, InsertWebReferralArgs};
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Record a web referral for analytics tracking.
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Web Referrals",
|
||||
path = "/v1/web_referrals/record",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = LogWebReferralResponse),
|
||||
(status = 500, description = "Server error"),
|
||||
),
|
||||
)]
|
||||
pub async fn log_web_referral_handler(
|
||||
http_request: HttpRequest,
|
||||
request: web::Json<LogWebReferralRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>,
|
||||
) -> Result<HttpResponse, CommonWebError> {
|
||||
|
||||
// 1. Try to get the referral URL from the request body, falling back to the Referer header.
|
||||
let referral_url = request.maybe_referral_url.as_deref()
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| {
|
||||
http_request.headers().get("referer")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
// 2. If no URL from either source, early exit 200 OK.
|
||||
let referral_url = match referral_url {
|
||||
Some(url) => url,
|
||||
None => {
|
||||
return ok_response();
|
||||
}
|
||||
};
|
||||
|
||||
// 3. Parse the URL (infallible — we don't fail on bad URLs).
|
||||
// NB: Parse before truncating so we don't lose UTM params.
|
||||
let parsed = Url::parse(&referral_url).ok();
|
||||
|
||||
// Truncate the URL to 255 characters for storage.
|
||||
let referral_url: String = referral_url.chars().take(255).collect();
|
||||
|
||||
// 4. Extract domain if URL parsed.
|
||||
let maybe_domain = parsed.as_ref()
|
||||
.and_then(|u| u.host_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
// 5. Extract UTM parameters if URL parsed.
|
||||
let utm_source = extract_query_param(parsed.as_ref(), "utm_source");
|
||||
let utm_medium = extract_query_param(parsed.as_ref(), "utm_medium");
|
||||
let utm_campaign = extract_query_param(parsed.as_ref(), "utm_campaign");
|
||||
|
||||
// 6. Grab the request IP address.
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
// 7. Check inbound cookies for AVT. If not present, mint a new one.
|
||||
let existing_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
let (avt_token, maybe_new_avt_cookie) = match existing_avt_token {
|
||||
Some(token) => (token, None),
|
||||
None => {
|
||||
let token = AnonymousVisitorTrackingToken::generate();
|
||||
let cookie = server_state
|
||||
.avt_cookie_manager
|
||||
.make_new_cookie_with_apriori_token(&token)
|
||||
.map_err(|e| {
|
||||
warn!("AVT cookie creation error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
(token, Some(cookie))
|
||||
}
|
||||
};
|
||||
|
||||
let avt_token_str = avt_token.to_string();
|
||||
|
||||
// Truncate UTM fields to fit the schema.
|
||||
let utm_source = utm_source.map(|s| s.chars().take(32).collect::<String>());
|
||||
let utm_medium = utm_medium.map(|s| s.chars().take(32).collect::<String>());
|
||||
let utm_campaign = utm_campaign.map(|s| s.chars().take(255).collect::<String>());
|
||||
|
||||
// Insert into the database.
|
||||
insert_web_referral(&server_state.mysql_pool, InsertWebReferralArgs {
|
||||
url: &referral_url,
|
||||
maybe_domain: maybe_domain.as_deref(),
|
||||
utm_source: utm_source.as_deref(),
|
||||
utm_medium: utm_medium.as_deref(),
|
||||
utm_campaign: utm_campaign.as_deref(),
|
||||
ip_address: &ip_address,
|
||||
maybe_anonymous_visitor_token: Some(&avt_token_str),
|
||||
}).await.map_err(|e| {
|
||||
warn!("Error inserting web referral: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
info!("Recorded web referral: {}", referral_url);
|
||||
|
||||
// Build response, setting the AVT cookie if we minted a new one.
|
||||
let response = LogWebReferralResponse {
|
||||
success: true,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_| CommonWebError::ServerError)?;
|
||||
|
||||
let mut response_builder = HttpResponse::Ok();
|
||||
|
||||
if let Some(cookie) = maybe_new_avt_cookie {
|
||||
response_builder.cookie(cookie);
|
||||
}
|
||||
|
||||
Ok(response_builder
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
|
||||
fn ok_response() -> Result<HttpResponse, CommonWebError> {
|
||||
let response = LogWebReferralResponse {
|
||||
success: true,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_| CommonWebError::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
|
||||
fn extract_query_param(parsed: Option<&Url>, key: &str) -> Option<String> {
|
||||
parsed.and_then(|u| {
|
||||
u.query_pairs()
|
||||
.find(|(k, _)| k == key)
|
||||
.map(|(_, v)| v.to_string())
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod log_web_referral_handler;
|
||||
+2
@@ -14,6 +14,7 @@ use crate::http_server::routes::application_routes::user_bookmarks_routes::add_u
|
||||
use crate::http_server::routes::application_routes::user_rating_routes::add_user_rating_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;
|
||||
use crate::http_server::routes::application_routes::webhook_routes::add_webhook_routes;
|
||||
use crate::http_server::routes::application_routes::weights_routes::add_weights_routes;
|
||||
use actix_http::body::MessageBody;
|
||||
@@ -55,6 +56,7 @@ where
|
||||
|
||||
// Other useful tools
|
||||
app = add_analytics_routes(app); // /v1/analytics/...
|
||||
app = add_web_referrals_routes(app); // /v1/web_referrals/...
|
||||
|
||||
// User and user-adjacent routes
|
||||
app = add_comments_routes(app); // /v1/comments/...
|
||||
|
||||
@@ -14,6 +14,7 @@ mod user_bookmarks_routes;
|
||||
mod user_rating_routes;
|
||||
mod user_routes;
|
||||
mod wallet_routes;
|
||||
mod web_referrals_routes;
|
||||
mod webhook_routes;
|
||||
mod weights_routes;
|
||||
pub (super) mod add_application_routes;
|
||||
+49
@@ -19,11 +19,19 @@ use crate::http_server::endpoints::moderation::ip_bans::add_ip_ban::add_ip_ban_h
|
||||
use crate::http_server::endpoints::moderation::ip_bans::delete_ip_ban::delete_ip_ban_handler;
|
||||
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::wallet_ledger_entries::moderator_get_wallet_ledger_entry_handler::moderator_get_wallet_ledger_entry_handler;
|
||||
use crate::http_server::endpoints::moderation::wallets::list_user_wallets_handler::list_user_wallets_handler;
|
||||
use crate::http_server::endpoints::moderation::wallets::moderator_add_banked_balance_to_wallet_handler::moderator_add_banked_balance_to_wallet_handler;
|
||||
use crate::http_server::endpoints::moderation::wallets::moderator_get_wallet_handler::moderator_get_wallet_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;
|
||||
use crate::http_server::endpoints::moderation::jobs::kill_tts_inference_jobs::kill_tts_inference_jobs_handler;
|
||||
use crate::http_server::endpoints::moderation::user_bans::ban_user::ban_user_handler;
|
||||
use crate::http_server::endpoints::moderation::user_bans::list_banned_users::list_banned_users_handler;
|
||||
use crate::http_server::endpoints::moderation::user::moderator_user_lookup_by_stripe_customer_id_handler::moderator_user_lookup_by_stripe_customer_id_handler;
|
||||
use crate::http_server::endpoints::moderation::user::moderator_user_lookup_handler::moderator_user_lookup_handler;
|
||||
use crate::http_server::endpoints::moderation::user_feature_flags::edit_user_feature_flags_handler::edit_user_feature_flags_handler;
|
||||
|
||||
pub fn add_moderator_routes<T, B> (app: App<T>) -> App<T>
|
||||
@@ -42,7 +50,48 @@ pub fn add_moderator_routes<T, B> (app: App<T>) -> App<T>
|
||||
.route(web::post().to(edit_user_feature_flags_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::scope("/users")
|
||||
.service(web::resource("/lookup")
|
||||
.route(web::post().to(moderator_user_lookup_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/lookup_by_stripe_customer_id")
|
||||
.route(web::post().to(moderator_user_lookup_by_stripe_customer_id_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(web::scope("/jobs")
|
||||
.service(web::resource("/user/{user_token}/list")
|
||||
.route(web::get().to(list_user_jobs_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(web::resource("/wallet/{wallet_token}")
|
||||
.route(web::get().to(moderator_get_wallet_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/wallet/{wallet_token}/add_banked_balance")
|
||||
.route(web::post().to(moderator_add_banked_balance_to_wallet_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::scope("/wallets")
|
||||
.service(web::resource("/user/{user_token}/list")
|
||||
.route(web::get().to(list_user_wallets_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(web::resource("/wallet_ledger_entry/{wallet_ledger_entry_token}")
|
||||
.route(web::get().to(moderator_get_wallet_ledger_entry_handler))
|
||||
.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")
|
||||
.service(web::resource("/staff")
|
||||
.route(web::get().to(list_staff_handler))
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
use actix_http::body::MessageBody;
|
||||
use actix_service::ServiceFactory;
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
use actix_web::error::Error;
|
||||
use actix_web::{web, App, HttpResponse};
|
||||
|
||||
use crate::http_server::endpoints::web_referrals::log_web_referral_handler::log_web_referral_handler;
|
||||
|
||||
pub fn add_web_referrals_routes<T, B> (app: App<T>) -> App<T>
|
||||
where
|
||||
B: MessageBody,
|
||||
T: ServiceFactory<
|
||||
ServiceRequest,
|
||||
Config = (),
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
>,
|
||||
{
|
||||
app.service(web::scope("/v1/web_referrals")
|
||||
.service(web::resource("/record")
|
||||
.route(web::post().to(log_web_referral_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
}
|
||||
+2
-2
@@ -28,7 +28,7 @@ impl Display for RequireModeratorError {
|
||||
impl Error for RequireModeratorError {}
|
||||
|
||||
pub enum UseDatabase<'a> {
|
||||
Implicit,
|
||||
GrabNewConnection,
|
||||
FromPool(&'a mut PoolConnection<MySql>),
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ pub async fn require_moderator(
|
||||
let mut saved_connection = None;
|
||||
|
||||
let mysql_connection = match database {
|
||||
UseDatabase::Implicit => {
|
||||
UseDatabase::GrabNewConnection => {
|
||||
let mut connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await
|
||||
|
||||
@@ -30,9 +30,11 @@ import {
|
||||
getProviderDisplayName,
|
||||
getProviderIconByName,
|
||||
} from "@storyteller/model-list";
|
||||
import { Viewer3D } from "@storyteller/ui-viewer-3d";
|
||||
import Seo from "../../components/seo";
|
||||
|
||||
const VIDEO_EXTENSIONS = [".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"];
|
||||
const MODEL_3D_EXTENSIONS = [".glb", ".gltf", ".fbx", ".spz"];
|
||||
const COPY_FEEDBACK_DURATION = 1500;
|
||||
const SHARE_URL_BASE = "https://getartcraft.com/media/";
|
||||
|
||||
@@ -41,6 +43,11 @@ const isVideoUrl = (url: string): boolean => {
|
||||
return VIDEO_EXTENSIONS.some((ext) => urlLower.includes(ext));
|
||||
};
|
||||
|
||||
const is3DModelUrl = (url: string): boolean => {
|
||||
const urlLower = url.toLowerCase();
|
||||
return MODEL_3D_EXTENSIONS.some((ext) => urlLower.includes(ext));
|
||||
};
|
||||
|
||||
interface ContextImage {
|
||||
media_links: { cdn_url: string; maybe_thumbnail_template: string };
|
||||
media_token: string;
|
||||
@@ -52,6 +59,7 @@ interface MediaData {
|
||||
token: string | null;
|
||||
createdAt: string | null;
|
||||
isVideo: boolean;
|
||||
is3D: boolean;
|
||||
isLoaded: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
@@ -140,6 +148,7 @@ export default function MediaPage() {
|
||||
token: null,
|
||||
createdAt: null,
|
||||
isVideo: false,
|
||||
is3D: false,
|
||||
isLoaded: false,
|
||||
creator: null,
|
||||
});
|
||||
@@ -170,6 +179,7 @@ export default function MediaPage() {
|
||||
token: file.token || id,
|
||||
createdAt: file.created_at || null,
|
||||
isVideo: url ? isVideoUrl(url) : false,
|
||||
is3D: url ? is3DModelUrl(url) : false,
|
||||
isLoaded: isSameUrl ? prev.isLoaded : false,
|
||||
width: isSameUrl ? prev.width : undefined,
|
||||
height: isSameUrl ? prev.height : undefined,
|
||||
@@ -200,6 +210,7 @@ export default function MediaPage() {
|
||||
token: null,
|
||||
createdAt: null,
|
||||
isVideo: false,
|
||||
is3D: false,
|
||||
isLoaded: false,
|
||||
creator: null,
|
||||
});
|
||||
@@ -211,6 +222,7 @@ export default function MediaPage() {
|
||||
token: null,
|
||||
createdAt: null,
|
||||
isVideo: false,
|
||||
is3D: false,
|
||||
isLoaded: false,
|
||||
creator: null,
|
||||
});
|
||||
@@ -266,7 +278,13 @@ export default function MediaPage() {
|
||||
</div>
|
||||
) : media.url ? (
|
||||
<div className="relative h-full w-full flex items-center justify-center">
|
||||
{media.isVideo ? (
|
||||
{media.is3D ? (
|
||||
<Viewer3D
|
||||
modelUrl={addCorsParam(media.url) || media.url}
|
||||
isActive
|
||||
className="h-full w-full"
|
||||
/>
|
||||
) : media.isVideo ? (
|
||||
<video
|
||||
src={addCorsParam(media.url) || media.url}
|
||||
className="h-full w-full object-contain"
|
||||
|
||||
@@ -353,7 +353,7 @@ export function Viewer3D({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative h-full w-full overflow-hidden rounded-xl bg-[#282828] ${className}`}
|
||||
className={`relative h-full w-full overflow-hidden rounded-l-xl bg-[#282828] ${className}`}
|
||||
onMouseDown={stopPropagation}
|
||||
onMouseMove={stopPropagation}
|
||||
onMouseUp={stopPropagation}
|
||||
|
||||
Reference in New Issue
Block a user