mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 13:02:37 -05:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9a1130bde |
-168
@@ -1,168 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::user_subscriptions::UserSubscriptionToken`,\n user_token as `user_token: tokens::tokens::users::UserToken`,\n subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n subscription_product_slug,\n maybe_stripe_customer_id,\n maybe_stripe_product_id,\n maybe_stripe_subscription_id,\n maybe_stripe_subscription_status,\n maybe_stripe_invoice_is_paid,\n subscription_start_at,\n subscription_expires_at,\n current_billing_period_end_at,\n maybe_cancel_at,\n maybe_canceled_at\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_namespace = ?\n AND maybe_stripe_customer_id IS NOT NULL\n AND maybe_stripe_subscription_id IS NOT NULL\n AND maybe_stripe_subscription_status IS NOT NULL\n AND maybe_stripe_subscription_status NOT IN ('canceled', 'incomplete_expired')\n AND subscription_expires_at > CURRENT_TIMESTAMP\n ORDER BY id ASC\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::user_subscriptions::UserSubscriptionToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"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": 3,
|
||||
"name": "subscription_product_slug",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "maybe_stripe_customer_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_stripe_product_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_stripe_subscription_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_stripe_subscription_status",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_stripe_invoice_is_paid",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "subscription_start_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "subscription_expires_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "current_billing_period_end_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_cancel_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "BINARY",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_canceled_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "BINARY",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1e08676857e999a58d3c85474c2a2acec38a413e53ca4207aade690e0bfffe25"
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::wallets::WalletToken`,\n wallet_namespace as `namespace: enums::common::payments_namespace::PaymentsNamespace`,\n owner_user_token as `owner_user_token: tokens::tokens::users::UserToken`,\n banked_credits,\n monthly_credits\nFROM wallets\nWHERE token = ?\nLIMIT 1\nFOR UPDATE\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": "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",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "monthly_credits",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2be78dc9fada8cda24df59ee9911118aa6f40247b10c6a76e07cfa74fd7f1412"
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n SELECT\n token as `token: tokens::tokens::wallets::WalletToken`\n FROM wallets\n WHERE owner_user_token = ?\n AND wallet_namespace = ?\n ORDER BY id ASC\n LIMIT 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
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2c98facb81a43364997db5558ad0f12300230691b09ce96d49e530f8361c237f"
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n user_token,\n subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n subscription_product_slug,\n subscription_expires_at\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_expires_at > CURRENT_TIMESTAMP\n ORDER BY id ASC\n ",
|
||||
"query": "\nSELECT\n user_token,\n subscription_namespace,\n subscription_product_slug,\n subscription_expires_at\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_expires_at > CURRENT_TIMESTAMP\n ORDER BY id ASC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "subscription_namespace: enums::common::payments_namespace::PaymentsNamespace",
|
||||
"name": "subscription_namespace",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
@@ -54,5 +54,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "680d60fcda0f6fdf269f3525a3fe582db847e56ca7380c0ff5696cbd6efb7db3"
|
||||
"hash": "46e1cb058bffbea5a5d3ffa5dd904416d6f34ccefdb394030f19cc762b507d68"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE wallets\n SET\n monthly_credits = ?,\n version = version + 1\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "47b14121f41b98b0dc92772e9f58a3f4a3821b11ebb10b768595d7635e472dbb"
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::user_subscriptions::UserSubscriptionToken`,\n user_token as `user_token: tokens::tokens::users::UserToken`,\n subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n subscription_product_slug,\n maybe_stripe_subscription_status,\n maybe_stripe_customer_id,\n maybe_stripe_product_id,\n maybe_stripe_subscription_id\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_namespace = ?\n AND maybe_stripe_customer_id IS NOT NULL\n AND maybe_stripe_subscription_id IS NOT NULL\n AND maybe_stripe_subscription_status IS NOT NULL\n AND maybe_stripe_subscription_status NOT IN ('canceled', 'incomplete_expired')\n AND subscription_expires_at > CURRENT_TIMESTAMP\n ORDER BY id ASC\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::user_subscriptions::UserSubscriptionToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"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": 3,
|
||||
"name": "subscription_product_slug",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "maybe_stripe_subscription_status",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_stripe_customer_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_stripe_product_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_stripe_subscription_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "4bfa96682bd1e7576c26c89e3d1a17162193f7df3c1530122ca8e715afc5f385"
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::user_subscriptions::UserSubscriptionToken`,\n user_token as `user_token: tokens::tokens::users::UserToken`,\n subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n subscription_product_slug,\n maybe_stripe_subscription_status,\n maybe_stripe_customer_id,\n maybe_stripe_product_id,\n maybe_stripe_subscription_id\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_namespace = ?\n AND maybe_stripe_customer_id IS NOT NULL\n AND maybe_stripe_subscription_id IS NOT NULL\n AND maybe_stripe_subscription_status IS NOT NULL\n ORDER BY id ASC\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::user_subscriptions::UserSubscriptionToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"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": 3,
|
||||
"name": "subscription_product_slug",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "maybe_stripe_subscription_status",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_stripe_customer_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_stripe_product_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_stripe_subscription_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "4c2a71386362fb54f38bd6633f8aebe75e0bd8edf1b1b8773c6298c1f6018332"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO wallets\nSET\n token = ?,\n wallet_namespace = ?,\n\n owner_user_token = ?,\n \n banked_credits = 0,\n monthly_credits = 0\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4f5100a2cfb465964e77eaacb1ae394daae94de20c56a0966a8222dc9bea63c6"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO user_stripe_customer_links\nSET\n user_token = ?,\n payments_namespace = ?,\n stripe_customer_id = ?\n\nON DUPLICATE KEY UPDATE\n updated_at = CURRENT_TIMESTAMP\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6df1b3d2cd145fa3199898fe53565eef28a74206093f4fc6585aad6cc2e9ae71"
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n user_token as `user_token: tokens::tokens::users::UserToken`,\n payments_namespace as `payments_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n stripe_customer_id\n\nFROM user_stripe_customer_links\n\nWHERE\n user_token = ?\n AND payments_namespace = ?\n ORDER BY id ASC\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "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": 1,
|
||||
"name": "payments_namespace: enums::common::payments_namespace::PaymentsNamespace",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "stripe_customer_id",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "771ebd8c236cc3bfeee2df7168c05f639b666e5d67a46593332ce02a0316b4f9"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO user_subscriptions\nSET\n token = ?,\n\n maybe_stripe_invoice_is_paid = ?,\n\n user_token = ?,\n subscription_namespace = ?,\n subscription_product_slug = ?,\n\n maybe_stripe_subscription_id = ?,\n maybe_stripe_customer_id = ?,\n\n maybe_stripe_product_id = ?,\n maybe_stripe_price_id = ?,\n\n maybe_stripe_recurring_interval = ?,\n maybe_stripe_subscription_status = ?,\n maybe_stripe_is_production = ?,\n \n maybe_stripe_billing_cycle_anchor = ?,\n\n subscription_start_at = ?,\n current_billing_period_start_at = ?,\n current_billing_period_end_at = ?,\n subscription_expires_at = ?,\n maybe_cancel_at = ?,\n maybe_canceled_at = ?,\n\n version = version + 1\n\nON DUPLICATE KEY UPDATE\n maybe_stripe_invoice_is_paid = ?,\n\n subscription_namespace = ?,\n subscription_product_slug= ?,\n\n maybe_stripe_product_id = ?,\n maybe_stripe_price_id = ?,\n\n maybe_stripe_recurring_interval = ?,\n maybe_stripe_subscription_status = ?,\n \n maybe_stripe_billing_cycle_anchor = ?,\n\n current_billing_period_start_at = ?,\n current_billing_period_end_at = ?,\n subscription_expires_at = ?,\n maybe_cancel_at = ?,\n maybe_canceled_at = ?,\n\n version = version + 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 32
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "871163ef30d2f6767f836a05f46d2ce7fb4733583eedddbf07732ebb5c1f1b53"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO user_subscriptions\nSET\n token = ?,\n user_token = ?,\n subscription_namespace = ?,\n subscription_product_slug = ?,\n\n maybe_stripe_subscription_id = ?,\n maybe_stripe_customer_id = ?,\n\n maybe_stripe_product_id = ?,\n maybe_stripe_price_id = ?,\n\n maybe_stripe_recurring_interval = ?,\n maybe_stripe_subscription_status = ?,\n maybe_stripe_is_production = ?,\n\n subscription_start_at = ?,\n current_billing_period_start_at = ?,\n current_billing_period_end_at = ?,\n subscription_expires_at = ?,\n maybe_cancel_at = ?,\n maybe_canceled_at = ?,\n\n version = version + 1\n\nON DUPLICATE KEY UPDATE\n subscription_namespace = ?,\n subscription_product_slug= ?,\n\n maybe_stripe_product_id = ?,\n maybe_stripe_price_id = ?,\n\n maybe_stripe_recurring_interval = ?,\n maybe_stripe_subscription_status = ?,\n\n current_billing_period_start_at = ?,\n current_billing_period_end_at = ?,\n subscription_expires_at = ?,\n maybe_cancel_at = ?,\n maybe_canceled_at = ?,\n\n version = version + 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 28
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8d267a609157e6459393ffa32ea83996dcf135c52ee8a930d5be628802d97371"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO wallet_ledger_entries\nSET\n token = ?,\n wallet_token = ?,\n entry_type = ?,\n maybe_entity_ref = ?,\n credits_delta = ?,\n banked_credits_before = ?,\n banked_credits_after = ?,\n monthly_credits_before = ?,\n monthly_credits_after = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9d26917668d4f0ecd2492a94a7775e4561fc5d8b33194274a7715b9853ecea00"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO user_subscriptions\nSET\n token = ?,\n user_token = ?,\n subscription_namespace = ?,\n subscription_product_slug = ?,\n\n maybe_stripe_subscription_id = ?,\n maybe_stripe_customer_id = ?,\n\n maybe_stripe_product_id = ?,\n maybe_stripe_price_id = ?,\n\n maybe_stripe_recurring_interval = ?,\n maybe_stripe_subscription_status = ?,\n maybe_stripe_is_production = ?,\n \n maybe_stripe_billing_cycle_anchor = ?,\n\n subscription_start_at = ?,\n current_billing_period_start_at = ?,\n current_billing_period_end_at = ?,\n subscription_expires_at = ?,\n maybe_cancel_at = ?,\n maybe_canceled_at = ?,\n\n version = version + 1\n\nON DUPLICATE KEY UPDATE\n subscription_namespace = ?,\n subscription_product_slug= ?,\n\n maybe_stripe_product_id = ?,\n maybe_stripe_price_id = ?,\n\n maybe_stripe_recurring_interval = ?,\n maybe_stripe_subscription_status = ?,\n \n maybe_stripe_billing_cycle_anchor = ?,\n\n current_billing_period_start_at = ?,\n current_billing_period_end_at = ?,\n subscription_expires_at = ?,\n maybe_cancel_at = ?,\n maybe_canceled_at = ?,\n\n version = version + 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 30
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a28d6de7424b269e64e97692a4c67a12fcfb789a3e5a512a103a78dea60e75ce"
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n SELECT\n token as `token: tokens::tokens::wallets::WalletToken`,\n wallet_namespace as `namespace: enums::common::payments_namespace::PaymentsNamespace`,\n banked_credits,\n monthly_credits\n FROM wallets\n WHERE owner_user_token = ?\n AND wallet_namespace = ?\n ORDER BY id ASC\n LIMIT 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": "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",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "monthly_credits",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "NOT_NULL | UNSIGNED",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ac8e3bc9f4e0722ec2f805dbb52b8e43ec8f7a1aa923b9f8f5f6c0770a315ee6"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE wallets\n SET\n banked_credits = ?,\n version = version + 1\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b721e3fda6d80df5041c918b1a127236c802ed18ccc961cc6ac96d156bf085da"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE wallets\n SET\n banked_credits = ?,\n monthly_credits = ?,\n version = version + 1\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c7f20753f9704d5ff3358586240ecc8a505990f4634ba3af9de6c99c22aa7368"
|
||||
}
|
||||
Generated
+3
-258
@@ -926,155 +926,11 @@ dependencies = [
|
||||
"serde_qs 0.10.1",
|
||||
"sha2 0.10.8",
|
||||
"smart-default 0.6.0",
|
||||
"smol_str 0.1.24",
|
||||
"smol_str",
|
||||
"thiserror 1.0.63",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cecbddf002ad7a13d2041eadf1b234cb3f57653ffdd901a01bc3f1c65aa77440"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hmac 0.12.1",
|
||||
"http-types",
|
||||
"hyper 0.14.28",
|
||||
"hyper-tls 0.5.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_qs 0.10.1",
|
||||
"sha2 0.10.8",
|
||||
"smart-default 0.6.0",
|
||||
"smol_str 0.1.24",
|
||||
"thiserror 1.0.63",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92140fa7152f9dcde7554cb1da37c7ae93b0c724db5214c754c55a9765afbe8"
|
||||
dependencies = [
|
||||
"async-stripe-client-core",
|
||||
"async-stripe-shared",
|
||||
"bytes 1.10.1",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper-tls 0.6.0",
|
||||
"hyper-util",
|
||||
"miniserde",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-billing"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33c4cd0ac61e0dcb39da4f3c04b049c0d8a3a98868ca976e18ff56ecac1b6e08"
|
||||
dependencies = [
|
||||
"async-stripe-client-core",
|
||||
"async-stripe-shared",
|
||||
"async-stripe-types",
|
||||
"miniserde",
|
||||
"serde",
|
||||
"smol_str 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-checkout"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b115740c799730d4caac099d48be4908d2d99611d56a874d2d3555b5d4306a97"
|
||||
dependencies = [
|
||||
"async-stripe-client-core",
|
||||
"async-stripe-shared",
|
||||
"async-stripe-types",
|
||||
"miniserde",
|
||||
"serde",
|
||||
"smol_str 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-client-core"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb1f6b6693946e650a9361feb319389a1408534ec1b618cd9239f1e2e20b8668"
|
||||
dependencies = [
|
||||
"async-stripe-shared",
|
||||
"async-stripe-types",
|
||||
"bytes 1.10.1",
|
||||
"futures-util",
|
||||
"miniserde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_qs 0.13.0",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-core"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81d0445a38170bcc3e4adf52fd80c94b70b02dc73a102afcd0bc9d6051eada18"
|
||||
dependencies = [
|
||||
"async-stripe-client-core",
|
||||
"async-stripe-shared",
|
||||
"async-stripe-types",
|
||||
"miniserde",
|
||||
"serde",
|
||||
"smol_str 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-shared"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b4328e5c0deeacd7cd5a95286d4f5dce045791b30be87583dd706c2342236c3"
|
||||
dependencies = [
|
||||
"async-stripe-types",
|
||||
"miniserde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol_str 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-types"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985b4f00fd57ce9cbd015c6410d05dfbe5d2bf90513e20b417e70b6f022e3f88"
|
||||
dependencies = [
|
||||
"miniserde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol_str 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stripe-webhook"
|
||||
version = "1.0.0-alpha.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75f8382af92463fa8e0b86e825d7d08eecbcd8715c197f1a66173fcc18e35774"
|
||||
dependencies = [
|
||||
"async-stripe-shared",
|
||||
"async-stripe-types",
|
||||
"chrono",
|
||||
"hex",
|
||||
"hmac 0.12.1",
|
||||
"miniserde",
|
||||
"serde",
|
||||
"sha2 0.10.8",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.7.1"
|
||||
@@ -1345,41 +1201,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "billing_artcraft_component"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"artcraft_api_defs",
|
||||
"async-stripe 1.0.0-alpha.2",
|
||||
"async-stripe-billing",
|
||||
"async-stripe-checkout",
|
||||
"async-stripe-core",
|
||||
"async-stripe-shared",
|
||||
"async-stripe-types",
|
||||
"async-stripe-webhook",
|
||||
"chrono",
|
||||
"component_traits",
|
||||
"enums",
|
||||
"errors",
|
||||
"http_server_common",
|
||||
"log",
|
||||
"mockall",
|
||||
"mysql_queries",
|
||||
"once_cell",
|
||||
"reusable_types",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokens",
|
||||
"tokio",
|
||||
"user_traits_component",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "billing_component"
|
||||
version = "0.0.1"
|
||||
@@ -1387,11 +1208,9 @@ dependencies = [
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"async-stripe 0.26.0",
|
||||
"async-stripe",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"component_traits",
|
||||
"enums",
|
||||
"errors",
|
||||
"http_server_common",
|
||||
"log",
|
||||
@@ -2270,34 +2089,6 @@ dependencies = [
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "component_traits"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"artcraft_api_defs",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"enums",
|
||||
"errors",
|
||||
"http_server_common",
|
||||
"log",
|
||||
"mockall",
|
||||
"mysql_queries",
|
||||
"once_cell",
|
||||
"reusable_types",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokens",
|
||||
"tokio",
|
||||
"user_traits_component",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composite_identifiers"
|
||||
version = "0.0.1"
|
||||
@@ -6607,17 +6398,6 @@ dependencies = [
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mini-internal"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c74ab4f1a0c0ab045260ee4727b23c00cc17e5eff5095262d08eef8c3c8d49"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minidom"
|
||||
version = "0.16.0"
|
||||
@@ -6633,17 +6413,6 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniserde"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08ec68bf2ad170a53a6efa92c3df7187968d6e475fe7a935725868154074ca0f"
|
||||
dependencies = [
|
||||
"itoa 1.0.11",
|
||||
"mini-internal",
|
||||
"ryu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
@@ -9759,17 +9528,6 @@ dependencies = [
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_qs"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.19"
|
||||
@@ -10146,16 +9904,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smol_str"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
@@ -10544,11 +10292,9 @@ dependencies = [
|
||||
"actix_helpers",
|
||||
"anyhow",
|
||||
"artcraft_api_defs",
|
||||
"async-stripe 0.26.0",
|
||||
"async-stripe 0.41.0",
|
||||
"async-stripe",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"billing_artcraft_component",
|
||||
"billing_component",
|
||||
"bootstrap",
|
||||
"bucket_paths",
|
||||
@@ -10556,7 +10302,6 @@ dependencies = [
|
||||
"chrono",
|
||||
"cloud_storage",
|
||||
"collections",
|
||||
"component_traits",
|
||||
"composite_identifiers",
|
||||
"config",
|
||||
"cookies",
|
||||
|
||||
+32
-37
@@ -6,7 +6,6 @@ resolver = "2"
|
||||
members = [
|
||||
# API clients for external services
|
||||
# These might be custom implementations or wrappers over other client libraries.
|
||||
"crates/api_clients/artcraft_api_defs", # TODO: api_clients/{artcraft}/{api_defs,client}
|
||||
"crates/api_clients/cloud_storage",
|
||||
"crates/api_clients/fal_client",
|
||||
"crates/api_clients/google_drive_common",
|
||||
@@ -98,6 +97,7 @@ members = [
|
||||
#"crates/schema/database/sqlite_queries",
|
||||
|
||||
# Schema (public): common strongly-typed tokens, ids, payloads, etc.
|
||||
"crates/schema/public/api/artcraft_api_defs",
|
||||
"crates/schema/public/composite_identifiers",
|
||||
"crates/schema/public/enums",
|
||||
"crates/schema/public/enums_public",
|
||||
@@ -119,8 +119,6 @@ members = [
|
||||
|
||||
# Service Plugins: complete subsystems that can be plugged into services (users, billing, etc.)
|
||||
"crates/service/plugins/billing",
|
||||
"crates/service/plugins/billing_artcraft",
|
||||
"crates/service/plugins/component_traits",
|
||||
"crates/service/plugins/inference_workloads",
|
||||
"crates/service/plugins/user_traits_component",
|
||||
|
||||
@@ -178,40 +176,6 @@ publish = false
|
||||
#
|
||||
workspace-build-acceleration = { path = "crates/workspace-build-acceleration" }
|
||||
|
||||
# Internal libs
|
||||
# Setting paths here ensures we don't need to do so in every crate's Cargo.toml
|
||||
# It makes refactoring much easier.
|
||||
artcraft_api_defs = { path = "crates/api_clients/artcraft_api_defs" }
|
||||
build_metadata = { path = "crates/lib/build_metadata" }
|
||||
cloud_storage = { path = "crates/api_clients/cloud_storage" }
|
||||
cloudflare_errors = { path = "crates/lib/cloudflare_errors" }
|
||||
component_traits = { path = "crates/service/plugins/component_traits" }
|
||||
cookie_store = { path = "crates/lib/cookie_store" }
|
||||
crockford = { path = "crates/lib/data/crockford" }
|
||||
enums = { path = "crates/schema/public/enums" }
|
||||
enums_public = { path = "crates/schema/public/enums_public" }
|
||||
errors = { path = "crates/lib/errors" }
|
||||
fal_client = { path = "crates/api_clients/fal_client" }
|
||||
file_to_base64_url = { path = "crates/lib/files/file_to_base64_url" }
|
||||
filesys = { path = "crates/lib/files/filesys" }
|
||||
idempotency = { path = "crates/lib/idempotency" }
|
||||
images = { path = "crates/lib/files/images" }
|
||||
jwt_light = { path = "crates/lib/jwt_light" }
|
||||
memory_caching = { path = "crates/lib/caching/memory_caching" }
|
||||
memory_store = { path = "crates/lib/caching/memory_store" }
|
||||
midjourney_client = { path = "crates/api_clients/midjourney_client" }
|
||||
mimetypes = { path = "crates/lib/files/mimetypes" }
|
||||
openai_sora_client = { path = "crates/api_clients/openai_sora_client" }
|
||||
primitives = { path = "crates/lib/data/primitives" }
|
||||
shared_service_components = { path = "crates/lib/shared_service_components" }
|
||||
sqlite_tasks = { path = "crates/schema/database/sqlite_tasks" }
|
||||
storyteller_client = { path = "crates/api_clients/storyteller_client" }
|
||||
subprocess_common = { path = "crates/lib/subprocess_common" }
|
||||
test_data = { path = "crates/lib/test_data" }
|
||||
testing = { path = "crates/lib/testing" }
|
||||
tokens = { path = "crates/schema/public/tokens" }
|
||||
web_base64 = { path = "crates/lib/data/web_base64" }
|
||||
|
||||
# External libs
|
||||
# Centrally pinned versions so that we can uniformly upgrade/downgrade versions across the workspace
|
||||
# If a crate needs to use an older/newer version, the dependency should be managed in that crate's own Cargo.toml
|
||||
@@ -255,3 +219,34 @@ thiserror = "2.0.12"
|
||||
tokio = { version = "1.45.1", features = ["macros"] }
|
||||
toml = "0.8.22"
|
||||
url = { version = "2.5.4", features = ["serde"] }
|
||||
|
||||
# Internal libs
|
||||
artcraft_api_defs = { path = "crates/schema/public/api/artcraft_api_defs" }
|
||||
build_metadata = { path = "crates/lib/build_metadata" }
|
||||
cloud_storage = { path = "crates/api_clients/cloud_storage" }
|
||||
cloudflare_errors = { path = "crates/lib/cloudflare_errors" }
|
||||
cookie_store = { path = "crates/lib/cookie_store" }
|
||||
crockford = { path = "crates/lib/data/crockford" }
|
||||
enums = { path = "crates/schema/public/enums" }
|
||||
enums_public = { path = "crates/schema/public/enums_public" }
|
||||
errors = { path = "crates/lib/errors" }
|
||||
fal_client = { path = "crates/api_clients/fal_client" }
|
||||
file_to_base64_url = { path = "crates/lib/files/file_to_base64_url" }
|
||||
filesys = { path = "crates/lib/files/filesys" }
|
||||
idempotency = { path = "crates/lib/idempotency" }
|
||||
images = { path = "crates/lib/files/images" }
|
||||
jwt_light = { path = "crates/lib/jwt_light" }
|
||||
memory_caching = { path = "crates/lib/caching/memory_caching" }
|
||||
memory_store = { path = "crates/lib/caching/memory_store" }
|
||||
midjourney_client = { path = "crates/api_clients/midjourney_client" }
|
||||
mimetypes = { path = "crates/lib/files/mimetypes" }
|
||||
openai_sora_client = { path = "crates/api_clients/openai_sora_client" }
|
||||
primitives = { path = "crates/lib/data/primitives" }
|
||||
shared_service_components = { path = "crates/lib/shared_service_components" }
|
||||
sqlite_tasks = { path = "crates/schema/database/sqlite_tasks" }
|
||||
storyteller_client = { path = "crates/api_clients/storyteller_client" }
|
||||
subprocess_common = { path = "crates/lib/subprocess_common" }
|
||||
test_data = { path = "crates/lib/test_data" }
|
||||
testing = { path = "crates/lib/testing" }
|
||||
tokens = { path = "crates/schema/public/tokens" }
|
||||
web_base64 = { path = "crates/lib/data/web_base64" }
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE user_subscriptions DROP COLUMN maybe_stripe_invoice_is_paid;
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE user_subscriptions
|
||||
ADD COLUMN maybe_stripe_invoice_is_paid BOOLEAN DEFAULT NULL
|
||||
AFTER maybe_stripe_subscription_status;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
DROP TABLE wallets;
|
||||
@@ -1,66 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
CREATE TABLE wallets (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- If we host multiple sites with distinct categories, this will enable us to
|
||||
-- segregate wallets. This is effectively a wallet namespace.
|
||||
-- NB: Uses the `PaymentsNamespace` enum, which is also shared with subscriptions.
|
||||
wallet_namespace VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Who owns the wallet
|
||||
-- In the future, we might allow other users to use the same wallet,
|
||||
-- but the wallet would still have an owner.
|
||||
-- For now, we'll keep this 1:1 with users, but we may want 1:n in the future.
|
||||
owner_user_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- The durable credits bucket.
|
||||
-- This bucket is persistent and is refilled by user credit purchases.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
banked_credits INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- The monthly credits bucket.
|
||||
-- This bucket is ephemeral and is refilled monthly.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
monthly_credits INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Whether the stripe subscription is active.
|
||||
-- subscription_is_active BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Whether the stripe subscription is paid.
|
||||
-- If the subscription is not paid, we should not allow usage of monthly credits.
|
||||
-- subscription_is_paid BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== PAYMENTS AND SUBSCRIPTIONS TIMESTAMPS ==========
|
||||
|
||||
-- When the user last purchased banked (durable) credits.
|
||||
-- banked_credits_last_purchased_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
-- When we last refilled the monthly bucket.
|
||||
-- This has to do with subscription logic.
|
||||
-- monthly_wallet_refilled_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
UNIQUE KEY (wallet_namespace, owner_user_token), -- For now
|
||||
INDEX idx_wallet_namespace (wallet_namespace),
|
||||
INDEX idx_owner_user_token (owner_user_token)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
DROP TABLE wallet_ledger_entries;
|
||||
@@ -1,52 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
CREATE TABLE wallet_ledger_entries (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Which wallet this entry corresponds to.
|
||||
wallet_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- The type of ledger entry this is.
|
||||
entry_type VARCHAR(32) NOT NULL,
|
||||
|
||||
-- A record we can look up to attribute the credit or deduction against
|
||||
-- This could be an internal entity token (prompt_token, job token) or an
|
||||
-- external token (stripe event_id, stripe invoice.id, etc.)
|
||||
-- Length is VARCHAR(255) to accommodate Stripe IDs.
|
||||
maybe_entity_ref VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- Signed integer that shows the delta of credits for this event.
|
||||
-- This combines impacts to both banked_credits and monthly_credits.
|
||||
-- This is less authoritative than the before/after fields, but is useful
|
||||
credits_delta INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of banked credits before the event.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
banked_credits_before INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of banked credits after the event.
|
||||
banked_credits_after INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of monthly credits before the event.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
monthly_credits_before INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of monthly credits after the event.
|
||||
monthly_credits_after INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
KEY index_wallet_token (wallet_token)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE user_subscriptions DROP COLUMN maybe_stripe_billing_cycle_anchor;
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE user_subscriptions
|
||||
ADD COLUMN maybe_stripe_billing_cycle_anchor TIMESTAMP DEFAULT NULL
|
||||
AFTER maybe_stripe_invoice_is_paid;
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE stripe_webhook_event_logs DROP COLUMN maybe_internal_entity_token;
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE stripe_webhook_event_logs
|
||||
ADD COLUMN maybe_internal_entity_token VARCHAR(32) DEFAULT NULL
|
||||
AFTER maybe_user_token;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
DROP TABLE user_stripe_customer_links;
|
||||
@@ -1,36 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- 1:1 links between a user and a stripe customer (per payments namespace)
|
||||
-- We can hard delete records if we have to.
|
||||
-- If users need a new stripe customer ID, we need to hard delete the existing record.
|
||||
CREATE TABLE user_stripe_customer_links (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Composite primary key
|
||||
user_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Composite primary key
|
||||
-- If we host multiple sites with distinct categories, this will enable us to
|
||||
-- segregate accounts.
|
||||
-- NB: Uses the `PaymentsNamespace` enum, which is also shared with subscriptions.
|
||||
payments_namespace VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Unique key. (We can loosen to `stripe_customer_id, wallet_namespace` later if
|
||||
-- we want to share billing.)
|
||||
stripe_customer_id VARCHAR(255) NOT NULL,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (user_token, payments_namespace),
|
||||
-- UNIQUE KEY (stripe_customer_id, payments_namespace)
|
||||
UNIQUE KEY (stripe_customer_id)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -1,66 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
CREATE TABLE stripe_webhook_event_logs (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- ========== STRIPE DATA ==========
|
||||
|
||||
-- You can safely assume object IDs we generate will never exceed 255 characters,
|
||||
-- but you should be able to handle IDs of up to that length. If for example you’re using MySQL,
|
||||
-- you should store IDs in a VARCHAR(255) COLLATE utf8_bin column (the COLLATE configuration ensures
|
||||
-- case-sensitivity in lookups).
|
||||
-- https://stackoverflow.com/a/61809494
|
||||
stripe_event_id VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The type of webhook event
|
||||
stripe_event_type VARCHAR(255) NOT NULL,
|
||||
|
||||
-- The id of the entity that was the subject of the event.
|
||||
-- We don't always extract this.
|
||||
maybe_stripe_event_entity_id VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- The id of the entity that was the subject of the event.
|
||||
-- We don't always extract this.
|
||||
maybe_stripe_customer_id VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- Whether this is in production or test mode in Stripe.
|
||||
-- This is controlled by which API keys are used.
|
||||
-- "livemode"=true in production.
|
||||
stripe_is_production BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- ========== INTERNAL SYSTEM METADATA ==========
|
||||
|
||||
-- Whether we handled the event with any sort of action
|
||||
action_was_taken BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Whether we should ignore any future retries by Stripe replaying the event.
|
||||
-- This helps us treat events with idempotency.
|
||||
should_ignore_retry BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Some event types may attach a user token
|
||||
maybe_user_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- An internal optional entity token this resolves to.
|
||||
-- eg. `wallets.token` or `user_subscriptions.token`.
|
||||
maybe_internal_entity_token VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- ========== TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- When the event was created on Stripe's end
|
||||
stripe_event_created_at TIMESTAMP NOT NULL,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (stripe_event_id),
|
||||
KEY index_stripe_event_type (stripe_event_type),
|
||||
KEY index_maybe_stripe_event_entity_id (maybe_stripe_event_entity_id),
|
||||
KEY index_maybe_stripe_customer_id (maybe_stripe_customer_id),
|
||||
KEY fk_maybe_user_token (maybe_user_token)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -1,36 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- 1:1 links between a user and a stripe customer (per payments namespace)
|
||||
-- We can hard delete records if we have to.
|
||||
-- If users need a new stripe customer ID, we need to hard delete the existing record.
|
||||
CREATE TABLE user_stripe_customer_links (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Composite primary key
|
||||
user_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Composite primary key
|
||||
-- If we host multiple sites with distinct categories, this will enable us to
|
||||
-- segregate accounts.
|
||||
-- NB: Uses the `PaymentsNamespace` enum, which is also shared with subscriptions.
|
||||
payments_namespace VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Unique key. (We can loosen to `stripe_customer_id, wallet_namespace` later if
|
||||
-- we want to share billing.)
|
||||
stripe_customer_id VARCHAR(255) NOT NULL,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (user_token, payments_namespace),
|
||||
-- UNIQUE KEY (stripe_customer_id, payments_namespace)
|
||||
UNIQUE KEY (stripe_customer_id)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -53,18 +53,6 @@ CREATE TABLE user_subscriptions (
|
||||
-- Subscription object status enum as string
|
||||
maybe_stripe_subscription_status VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- Primarily managed by the `invoice.paid` and `invoice.payment_failed` webhooks.
|
||||
-- NB: Not applicable to FakeYou subscriptions!
|
||||
maybe_stripe_invoice_is_paid BOOLEAN DEFAULT NULL,
|
||||
|
||||
-- Which day of the month, or month of the year, etc. to anchor the subscription to.
|
||||
-- See docs on the subscription object.
|
||||
-- Stripe:
|
||||
-- "The reference point that aligns future billing cycle dates. It sets the day
|
||||
-- of week for week intervals, the day of month for month and year intervals, and
|
||||
-- the month of year for year intervals. The timestamp is in UTC format."
|
||||
maybe_stripe_billing_cycle_anchor TIMESTAMP DEFAULT NULL,
|
||||
|
||||
maybe_stripe_is_production BOOLEAN DEFAULT NULL,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
@@ -76,8 +64,6 @@ CREATE TABLE user_subscriptions (
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- NB(bt, 2025-09-16): I don't think we write to the soft deleted_at field for user_subscriptions.
|
||||
deleted_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
-- ========== SUBSCRIPTION TIMESTAMPS ==========
|
||||
@@ -86,10 +72,7 @@ CREATE TABLE user_subscriptions (
|
||||
-- This may predate the Stripe object `created` timestamp due to backdating.
|
||||
subscription_start_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Our synthetic "expire" date. Moved forward for active subscriptions,
|
||||
-- backdated for canceled subscriptions.
|
||||
--
|
||||
-- TODO: We'll (maybe?) use this to determine if the subscription is active.
|
||||
-- We'll use this to determine if the subscription is active.
|
||||
--
|
||||
-- Always compare against MySQL's clock rather than the app's clock so that we
|
||||
-- don't get weird clock skew behaviors across multiple requests.
|
||||
@@ -99,12 +82,8 @@ CREATE TABLE user_subscriptions (
|
||||
subscription_expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Billing periods.
|
||||
-- Stripe API: subscription.items[0].data.current_period_start
|
||||
-- Maybe useful for debugging.
|
||||
current_billing_period_start_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Stripe API: subscription.items[0].data.current_period_end
|
||||
-- current_period_end is when the subscription renews (or cancels, if the
|
||||
-- subscription is set to cancel).
|
||||
current_billing_period_end_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Subscription cancellation (future and past)
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
CREATE TABLE wallet_ledger_entries (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Which wallet this entry corresponds to.
|
||||
wallet_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- The type of ledger entry this is.
|
||||
entry_type VARCHAR(16) NOT NULL,
|
||||
|
||||
-- A record we can look up to attribute the credit or deduction against
|
||||
-- This could be an internal entity token (prompt_token, job token) or an
|
||||
-- external token (stripe event_id, stripe invoice.id, etc.)
|
||||
-- Length is VARCHAR(255) to accommodate Stripe IDs.
|
||||
maybe_entity_ref VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- Signed integer that shows the delta of credits for this event.
|
||||
-- This combines impacts to both banked_credits and monthly_credits.
|
||||
-- This is less authoritative than the before/after fields, but is useful
|
||||
credits_delta INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of banked credits before the event.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
banked_credits_before INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of banked credits after the event.
|
||||
banked_credits_after INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of monthly credits before the event.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
monthly_credits_before INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Balance of monthly credits after the event.
|
||||
monthly_credits_after INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
KEY index_wallet_token (wallet_token)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -1,66 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
CREATE TABLE wallets (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Effective "primary key" (PUBLIC)
|
||||
token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- If we host multiple sites with distinct categories, this will enable us to
|
||||
-- segregate wallets. This is effectively a wallet namespace.
|
||||
-- NB: Uses the `PaymentsNamespace` enum, which is also shared with subscriptions.
|
||||
wallet_namespace VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Who owns the wallet
|
||||
-- In the future, we might allow other users to use the same wallet,
|
||||
-- but the wallet would still have an owner.
|
||||
-- For now, we'll keep this 1:1 with users, but we may want 1:n in the future.
|
||||
owner_user_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- The durable credits bucket.
|
||||
-- This bucket is persistent and is refilled by user credit purchases.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
banked_credits INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- The monthly credits bucket.
|
||||
-- This bucket is ephemeral and is refilled monthly.
|
||||
-- Max value unsigned: 4,294,967,295 (U32)
|
||||
monthly_credits INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Whether the stripe subscription is active.
|
||||
-- subscription_is_active BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- Whether the stripe subscription is paid.
|
||||
-- If the subscription is not paid, we should not allow usage of monthly credits.
|
||||
-- subscription_is_paid BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
-- ========== VECTOR CLOCK ==========
|
||||
|
||||
-- Incremented with every update.
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== PAYMENTS AND SUBSCRIPTIONS TIMESTAMPS ==========
|
||||
|
||||
-- When the user last purchased banked (durable) credits.
|
||||
-- banked_credits_last_purchased_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
-- When we last refilled the monthly bucket.
|
||||
-- This has to do with subscription logic.
|
||||
-- monthly_wallet_refilled_at TIMESTAMP DEFAULT NULL,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (token),
|
||||
UNIQUE KEY (wallet_namespace, owner_user_token), -- For now
|
||||
INDEX idx_wallet_namespace (wallet_namespace),
|
||||
INDEX idx_owner_user_token (owner_user_token)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -1,20 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct GetSessionCreditsResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Any free credits the user might have
|
||||
/// We might support daily free credits.
|
||||
pub free_credits: u64,
|
||||
|
||||
/// Any monthly refilling credits the user might have
|
||||
pub monthly_credits: u64,
|
||||
|
||||
/// Any banked credits the user might have.
|
||||
pub banked_credits: u64,
|
||||
|
||||
/// All the credit amounts added together.
|
||||
pub sum_total_credits: u64,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod get_session_credits;
|
||||
@@ -1,8 +0,0 @@
|
||||
pub mod common;
|
||||
pub mod credits;
|
||||
pub mod generate;
|
||||
pub mod jobs;
|
||||
pub mod media_file;
|
||||
pub mod prompts;
|
||||
pub mod stripe_artcraft;
|
||||
pub mod subscriptions;
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CREATE_CREDITS_PACK_CHECKOUT_URL_PATH: &str = "/v1/stripe_artcraft/checkout/credits_pack";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateCreditsPackCheckoutRequest {
|
||||
pub credits_pack: Option<ArtcraftCreditsPackSlug>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateCreditsPackCheckoutResponse {
|
||||
pub success: bool,
|
||||
pub stripe_checkout_redirect_url: String,
|
||||
}
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH: &str = "/v1/stripe_artcraft/checkout/subscription";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateSubscriptionCheckoutRequest {
|
||||
/// The (non-Stripe) internal identifier for the product or subscription.
|
||||
/// This will be translated into a Stripe identifier.
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
|
||||
pub cadence: Option<PlanBillingCadence>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug, Copy, Clone)]
|
||||
pub enum PlanBillingCadence {
|
||||
#[serde(rename = "monthly")]
|
||||
Monthly,
|
||||
|
||||
#[serde(rename = "yearly")]
|
||||
Yearly,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCreateSubscriptionCheckoutResponse {
|
||||
pub success: bool,
|
||||
pub stripe_checkout_redirect_url: String,
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CUSTOMER_PORTAL_CANCEL_PLAN_URL_PATH: &str = "/v1/stripe_artcraft/portal/cancel_plan";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalCancelPlanRequest {
|
||||
// TODO: Not sure if this is needed
|
||||
pub portal_config_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalCancelPlanResponse {
|
||||
pub success: bool,
|
||||
pub stripe_portal_url: String,
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CUSTOMER_PORTAL_MANAGE_PLAN_URL_PATH: &str = "/v1/stripe_artcraft/portal/manage_plan";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalManagePlanRequest {
|
||||
// TODO: Not sure if this is needed
|
||||
pub portal_config_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalManagePlanResponse {
|
||||
pub success: bool,
|
||||
pub stripe_portal_url: String,
|
||||
}
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CUSTOMER_PORTAL_SWITCH_PLAN_URL_PATH: &str = "/v1/stripe_artcraft/portal/switch_plan";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalSwitchPlanRequest {
|
||||
// TODO: Not sure if this is needed
|
||||
pub portal_config_id: Option<String>,
|
||||
|
||||
/// REQUIRED
|
||||
/// The plan we're switching to.
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
|
||||
/// REQUIRED
|
||||
/// The cadence for the plan billing.
|
||||
pub cadence: Option<PlanBillingCadenceConfirmation>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug, Copy, Clone)]
|
||||
pub enum PlanBillingCadenceConfirmation {
|
||||
#[serde(rename = "monthly")]
|
||||
Monthly,
|
||||
|
||||
#[serde(rename = "yearly")]
|
||||
Yearly,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalSwitchPlanResponse {
|
||||
pub success: bool,
|
||||
pub stripe_portal_url: String,
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const CUSTOMER_PORTAL_UPDATE_PAYMENT_METHOD_URL_PATH: &str = "/v1/stripe_artcraft/portal/update_payment_method";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalUpdatePaymentMethodRequest {
|
||||
// TODO: Not sure if this is needed
|
||||
pub portal_config_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StripeArtcraftCustomerPortalUpdatePaymentMethodResponse {
|
||||
pub success: bool,
|
||||
pub stripe_portal_url: String,
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
pub mod create_credits_pack_checkout;
|
||||
pub mod create_subscription_checkout;
|
||||
pub mod customer_portal_cancel_plan;
|
||||
pub mod customer_portal_manage_plan;
|
||||
pub mod customer_portal_switch_plan;
|
||||
pub mod customer_portal_update_payment_method;
|
||||
@@ -1,37 +0,0 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::user_subscriptions::UserSubscriptionToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
// TODO: Maybe make "artcraft_subscription" or "fakeyou_subscription" with strongly typed slugs?
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct GetSessionSubscriptionResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Information about the user's subscription, if any.
|
||||
pub active_subscription: Option<SubscriptionInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct SubscriptionInfo {
|
||||
/// Unique token for the subscription.
|
||||
pub subscription_token: UserSubscriptionToken,
|
||||
|
||||
/// Namespace for the subscription.
|
||||
pub namespace: PaymentsNamespace,
|
||||
|
||||
/// Local key for the subscription.
|
||||
pub product_slug: String,
|
||||
|
||||
/// If the subscription is currently active, this is the next billing date.
|
||||
pub next_bill_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// If the subscription is set to cancel, this is the end date of the subscription.
|
||||
pub subscription_end_at: Option<DateTime<Utc>>,
|
||||
|
||||
///// When the current billing period ends (either
|
||||
///// auto-renew/rebill date or auto-cancel/cancellation date).
|
||||
//pub current_billing_period_end_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod get_session_subscription;
|
||||
@@ -1,27 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_get_request::basic_json_get_request;
|
||||
use artcraft_api_defs::credits::get_session_credits::GetSessionCreditsResponse;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
|
||||
pub async fn get_session_credits(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
payments_namespace: PaymentsNamespace
|
||||
) -> Result<GetSessionCreditsResponse, StorytellerError> {
|
||||
let url_path = get_url_path(payments_namespace);
|
||||
|
||||
Ok(basic_json_get_request(
|
||||
api_host,
|
||||
&url_path,
|
||||
maybe_creds,
|
||||
).await?)
|
||||
}
|
||||
|
||||
|
||||
fn get_url_path(payments_namespace: PaymentsNamespace) -> String {
|
||||
let payments_namespace = payments_namespace.to_str();
|
||||
format!("/v1/credits/namespace/{}", payments_namespace)
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod get_session_credits;
|
||||
@@ -1,13 +1,10 @@
|
||||
//! An API client for Storyteller / FakeYou
|
||||
|
||||
pub mod credentials;
|
||||
pub mod credits;
|
||||
pub mod error;
|
||||
pub mod generate;
|
||||
pub mod jobs;
|
||||
pub mod media_files;
|
||||
pub mod prompts;
|
||||
pub mod recipes;
|
||||
pub mod stripe_artcraft;
|
||||
pub mod subscriptions;
|
||||
pub mod utils;
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::create_credits_pack_checkout::{StripeArtcraftCreateCreditsPackCheckoutRequest, StripeArtcraftCreateCreditsPackCheckoutResponse, CREATE_CREDITS_PACK_CHECKOUT_URL_PATH};
|
||||
|
||||
pub async fn create_credits_pack_checkout(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCreateCreditsPackCheckoutRequest,
|
||||
) -> Result<StripeArtcraftCreateCreditsPackCheckoutResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CREATE_CREDITS_PACK_CHECKOUT_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{StripeArtcraftCreateSubscriptionCheckoutRequest, StripeArtcraftCreateSubscriptionCheckoutResponse, CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH};
|
||||
|
||||
pub async fn create_subscription_checkout(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCreateSubscriptionCheckoutRequest,
|
||||
) -> Result<StripeArtcraftCreateSubscriptionCheckoutResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CREATE_SUBSCRIPTION_CHECKOUT_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_cancel_plan::{StripeArtcraftCustomerPortalCancelPlanRequest, StripeArtcraftCustomerPortalCancelPlanResponse, CUSTOMER_PORTAL_CANCEL_PLAN_URL_PATH};
|
||||
|
||||
pub async fn customer_portal_cancel_plan(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCustomerPortalCancelPlanRequest,
|
||||
) -> Result<StripeArtcraftCustomerPortalCancelPlanResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CUSTOMER_PORTAL_CANCEL_PLAN_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_manage_plan::{StripeArtcraftCustomerPortalManagePlanRequest, StripeArtcraftCustomerPortalManagePlanResponse, CUSTOMER_PORTAL_MANAGE_PLAN_URL_PATH};
|
||||
|
||||
pub async fn customer_portal_manage_plan(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCustomerPortalManagePlanRequest,
|
||||
) -> Result<StripeArtcraftCustomerPortalManagePlanResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CUSTOMER_PORTAL_MANAGE_PLAN_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::{StripeArtcraftCustomerPortalSwitchPlanRequest, StripeArtcraftCustomerPortalSwitchPlanResponse, CUSTOMER_PORTAL_SWITCH_PLAN_URL_PATH};
|
||||
|
||||
pub async fn customer_portal_switch_plan(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCustomerPortalSwitchPlanRequest,
|
||||
) -> Result<StripeArtcraftCustomerPortalSwitchPlanResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CUSTOMER_PORTAL_SWITCH_PLAN_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_post_request::basic_json_post_request;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_update_payment_method::{StripeArtcraftCustomerPortalUpdatePaymentMethodRequest, StripeArtcraftCustomerPortalUpdatePaymentMethodResponse, CUSTOMER_PORTAL_UPDATE_PAYMENT_METHOD_URL_PATH};
|
||||
|
||||
pub async fn customer_portal_update_payment_method(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
request: StripeArtcraftCustomerPortalUpdatePaymentMethodRequest,
|
||||
) -> Result<StripeArtcraftCustomerPortalUpdatePaymentMethodResponse, StorytellerError> {
|
||||
Ok(basic_json_post_request(
|
||||
api_host,
|
||||
CUSTOMER_PORTAL_UPDATE_PAYMENT_METHOD_URL_PATH,
|
||||
maybe_creds,
|
||||
request,
|
||||
).await?)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
pub mod create_credits_pack_checkout;
|
||||
pub mod create_subscription_checkout;
|
||||
pub mod customer_portal_cancel_plan;
|
||||
pub mod customer_portal_manage_plan;
|
||||
pub mod customer_portal_switch_plan;
|
||||
pub mod customer_portal_update_payment_method;
|
||||
@@ -1,28 +0,0 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_get_request::basic_json_get_request;
|
||||
use artcraft_api_defs::subscriptions::get_session_subscription::GetSessionSubscriptionResponse;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use log::debug;
|
||||
|
||||
pub async fn get_session_subscription(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
payments_namespace: PaymentsNamespace
|
||||
) -> Result<GetSessionSubscriptionResponse, StorytellerError> {
|
||||
let url_path = get_url_path(api_host, payments_namespace);
|
||||
|
||||
debug!("Requesting {:?}", &url_path);
|
||||
|
||||
Ok(basic_json_get_request(
|
||||
api_host,
|
||||
&url_path,
|
||||
maybe_creds,
|
||||
).await?)
|
||||
}
|
||||
|
||||
fn get_url_path(api_host: &ApiHost, payments_namespace: PaymentsNamespace) -> String {
|
||||
let payments_namespace = payments_namespace.to_str();
|
||||
format!("/v1/subscriptions/namespace/{}", payments_namespace)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod get_session_subscription;
|
||||
-3
@@ -41,7 +41,6 @@ use storyteller_client::media_files::get_media_file::get_media_file;
|
||||
use storyteller_client::utils::api_host::ApiHost;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EnqueueImageBgRemovalCommand {
|
||||
@@ -153,8 +152,6 @@ pub async fn enqueue_image_bg_removal_command(
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueImageBgRemovalSuccessResponse {}.into())
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -8,7 +8,6 @@ use crate::core::commands::response::failure_response_wrapper::{CommandErrorResp
|
||||
use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType};
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
@@ -223,8 +222,6 @@ pub async fn enqueue_contextual_edit_image_command(
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueContextualEditImageSuccessResponse {}.into())
|
||||
}
|
||||
|
||||
-3
@@ -10,7 +10,6 @@ use crate::core::commands::response::failure_response_wrapper::{CommandErrorResp
|
||||
use crate::core::commands::response::shorthand::{Response, ResponseOrErrorType};
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_failure_event::GenerationEnqueueFailureEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
@@ -213,8 +212,6 @@ pub async fn enqueue_image_inpaint_command(
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueImageInpaintSuccessResponse {}.into())
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -6,7 +6,6 @@ use crate::core::commands::response::failure_response_wrapper::{CommandErrorResp
|
||||
use crate::core::commands::response::shorthand::Response;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
@@ -147,8 +146,6 @@ pub async fn enqueue_image_to_3d_object_command(
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueImageTo3dObjectSuccessResponse {}.into())
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -6,7 +6,6 @@ use crate::core::commands::response::failure_response_wrapper::{CommandErrorResp
|
||||
use crate::core::commands::response::shorthand::Response;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
@@ -166,8 +165,6 @@ pub async fn enqueue_image_to_video_command(
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueImageToVideoSuccessResponse {}.into())
|
||||
}
|
||||
|
||||
-3
@@ -11,7 +11,6 @@ use crate::core::commands::response::failure_response_wrapper::{CommandErrorResp
|
||||
use crate::core::commands::response::shorthand::Response;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_enqueue_success_event::GenerationEnqueueSuccessEvent;
|
||||
use crate::core::events::sendable_event_trait::SendableEvent;
|
||||
@@ -217,8 +216,6 @@ pub async fn enqueue_text_to_image_command(
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to emit event: {:?}", err); // Fail open.
|
||||
}
|
||||
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
|
||||
Ok(EnqueueTextToImageSuccessResponse {}.into())
|
||||
}
|
||||
|
||||
@@ -40,14 +40,3 @@ impl From<&str> for CommandErrorResponseWrapper<(), ()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for CommandErrorResponseWrapper<(), ()> {
|
||||
fn from(value: String) -> Self {
|
||||
CommandErrorResponseWrapper {
|
||||
status: CommandErrorStatus::BadRequest, // NB: Default to 500 error.
|
||||
error_message: Some(value),
|
||||
error_type: None,
|
||||
error_details: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
|
||||
use enums::tauri::ux::tauri_event_name::TauriEventName;
|
||||
use serde_derive::Serialize;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct CreditsBalanceChangedEvent {
|
||||
// NB: We don't know how much was added or removed, just that it changed.
|
||||
// The frontend should re-query the balance
|
||||
}
|
||||
|
||||
impl BasicSendableEvent for CreditsBalanceChangedEvent {
|
||||
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::CreditsBalanceChangedEvent;
|
||||
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Success;
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
pub mod canvas_background_removal_complete_event;
|
||||
pub mod credits_balance_changed_event;
|
||||
pub mod image_edit_complete_event;
|
||||
pub mod refresh_account_state_event;
|
||||
pub mod show_provider_billing_modal_event;
|
||||
pub mod show_provider_login_modal_event;
|
||||
pub mod subscription_plan_changed_event;
|
||||
pub mod text_to_image_generation_complete_event;
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
|
||||
use enums::tauri::ux::tauri_event_name::TauriEventName;
|
||||
use serde_derive::Serialize;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct SubscriptionPlanChangedEvent {
|
||||
// NB: We don't know what plan was changed (subscribed, upgraded, canceled, etc.),
|
||||
// just that it changed. The actual fulfillment is async anyway.
|
||||
// The frontend should re-query the subscription plan after a short delay.
|
||||
}
|
||||
|
||||
impl BasicSendableEvent for SubscriptionPlanChangedEvent {
|
||||
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::SubscriptionPlanChangedEvent;
|
||||
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Success;
|
||||
}
|
||||
@@ -45,14 +45,6 @@ use crate::services::sora::commands::sora_logout_command::sora_logout_command;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::state::sora_task_queue::SoraTaskQueue;
|
||||
use crate::services::sora::threads::sora_task_polling_thread::sora_task_polling_thread;
|
||||
use crate::services::storyteller::commands::storyteller_get_credits_command::storyteller_get_credits_command;
|
||||
use crate::services::storyteller::commands::storyteller_get_subscription_command::storyteller_get_subscription_command;
|
||||
use crate::services::storyteller::commands::stripe_checkout::storyteller_open_credits_purchase_command::storyteller_open_credits_purchase_command;
|
||||
use crate::services::storyteller::commands::stripe_checkout::storyteller_open_subscription_purchase_command::storyteller_open_subscription_purchase_command;
|
||||
use crate::services::storyteller::commands::stripe_customer_portal::storyteller_open_customer_portal_cancel_plan_command::storyteller_open_customer_portal_cancel_plan_command;
|
||||
use crate::services::storyteller::commands::stripe_customer_portal::storyteller_open_customer_portal_manage_plan_command::storyteller_open_customer_portal_manage_plan_command;
|
||||
use crate::services::storyteller::commands::stripe_customer_portal::storyteller_open_customer_portal_switch_plan_command::storyteller_open_customer_portal_switch_plan_command;
|
||||
use crate::services::storyteller::commands::stripe_customer_portal::storyteller_open_customer_portal_update_payment_method_command::storyteller_open_customer_portal_update_payment_method_command;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use log::error;
|
||||
|
||||
@@ -104,7 +96,7 @@ pub fn run() {
|
||||
|
||||
println!("Initializing backend runtime...");
|
||||
|
||||
let builder = tauri::Builder::default()
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_upload::init())
|
||||
@@ -160,48 +152,36 @@ pub fn run() {
|
||||
.manage(midjourney_creds_manager)
|
||||
.manage(sora_creds_manager)
|
||||
.manage(sora_task_queue)
|
||||
.manage(storyteller_creds_manager_3);
|
||||
|
||||
// TODO: Break this out into another module, because RustRover/IntelliJ lags with these macros.
|
||||
// My first attempt at naively doing this didn't work because the macros can't find their codegen'd targets.
|
||||
let builder = builder.invoke_handler(tauri::generate_handler![
|
||||
check_sora_session_command,
|
||||
enqueue_contextual_edit_image_command,
|
||||
enqueue_image_bg_removal_command,
|
||||
enqueue_image_inpaint_command,
|
||||
enqueue_image_to_3d_object_command,
|
||||
enqueue_image_to_video_command,
|
||||
enqueue_text_to_image_command,
|
||||
fal_background_removal_command,
|
||||
fal_hunyuan_image_to_3d_command,
|
||||
fal_kling_image_to_video_command,
|
||||
flip_image,
|
||||
get_app_info_command,
|
||||
get_app_preferences_command,
|
||||
get_fal_api_key_command,
|
||||
get_provider_order_command,
|
||||
load_without_cors_command,
|
||||
midjourney_clear_credentials_command,
|
||||
midjourney_get_credential_info_command,
|
||||
midjourney_open_login_command,
|
||||
open_sora_login_command,
|
||||
platform_info_command,
|
||||
set_fal_api_key_command,
|
||||
set_provider_order_command,
|
||||
sora_image_generation_command,
|
||||
sora_image_remix_command,
|
||||
sora_logout_command,
|
||||
storyteller_get_credits_command,
|
||||
storyteller_get_subscription_command,
|
||||
storyteller_open_credits_purchase_command,
|
||||
storyteller_open_customer_portal_cancel_plan_command,
|
||||
storyteller_open_customer_portal_manage_plan_command,
|
||||
storyteller_open_customer_portal_switch_plan_command,
|
||||
storyteller_open_customer_portal_update_payment_method_command,
|
||||
storyteller_open_subscription_purchase_command,
|
||||
update_app_preferences_command,
|
||||
]);
|
||||
|
||||
builder.run(tauri::generate_context!("tauri.conf.json"))
|
||||
.manage(storyteller_creds_manager_3)
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
check_sora_session_command,
|
||||
enqueue_contextual_edit_image_command,
|
||||
enqueue_image_bg_removal_command,
|
||||
enqueue_image_inpaint_command,
|
||||
enqueue_image_to_3d_object_command,
|
||||
enqueue_image_to_video_command,
|
||||
enqueue_text_to_image_command,
|
||||
fal_background_removal_command,
|
||||
fal_hunyuan_image_to_3d_command,
|
||||
fal_kling_image_to_video_command,
|
||||
flip_image,
|
||||
get_app_info_command,
|
||||
get_app_preferences_command,
|
||||
get_fal_api_key_command,
|
||||
get_provider_order_command,
|
||||
load_without_cors_command,
|
||||
open_sora_login_command,
|
||||
midjourney_clear_credentials_command,
|
||||
midjourney_get_credential_info_command,
|
||||
midjourney_open_login_command,
|
||||
platform_info_command,
|
||||
set_fal_api_key_command,
|
||||
set_provider_order_command,
|
||||
sora_image_generation_command,
|
||||
sora_image_remix_command,
|
||||
sora_logout_command,
|
||||
update_app_preferences_command,
|
||||
])
|
||||
.run(tauri::generate_context!("tauri.conf.json"))
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod storyteller_get_credits_command;
|
||||
pub mod storyteller_get_subscription_command;
|
||||
pub mod stripe_checkout;
|
||||
pub mod stripe_customer_portal;
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::Provider;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, OpenStorytellerBillingWindowArgs, BillingWindowCase};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use storyteller_client::credits::get_session_credits::get_session_credits;
|
||||
use tauri::{AppHandle, State};
|
||||
use crate::core::commands::providers::get_provider_order_command::GetProviderOrderResponse;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
// TODO(bt,2025-09-12): Cache credits and clear cache on generate calls.
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct GetCreditsResponse {
|
||||
pub free_credits: u64,
|
||||
pub monthly_credits: u64,
|
||||
pub banked_credits: u64,
|
||||
pub sum_total_credits: u64,
|
||||
}
|
||||
|
||||
impl SerializeMarker for GetCreditsResponse {}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_get_credits_command(
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> ResponseOrErrorMessage<GetCreditsResponse> {
|
||||
|
||||
info!("storyteller_get_credits_command called");
|
||||
|
||||
let credits = get_credits(
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error getting credits: {:?}", err);
|
||||
format!("Error getting credits: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok(credits.into())
|
||||
}
|
||||
|
||||
async fn get_credits(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<GetCreditsResponse> {
|
||||
|
||||
let maybe_creds = storyteller_creds_manager.get_credentials()?;
|
||||
|
||||
let response = get_session_credits(
|
||||
&app_env_configs.storyteller_host,
|
||||
maybe_creds.as_ref(),
|
||||
PaymentsNamespace::Artcraft,
|
||||
).await?;
|
||||
|
||||
Ok(GetCreditsResponse {
|
||||
free_credits: response.free_credits,
|
||||
monthly_credits: response.monthly_credits,
|
||||
banked_credits: response.banked_credits,
|
||||
sum_total_credits: response.sum_total_credits,
|
||||
})
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::providers::get_provider_order_command::GetProviderOrderResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::Provider;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, OpenStorytellerBillingWindowArgs, BillingWindowCase};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use storyteller_client::credits::get_session_credits::get_session_credits;
|
||||
use storyteller_client::subscriptions::get_session_subscription::get_session_subscription;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::user_subscriptions::UserSubscriptionToken;
|
||||
// TODO(bt,2025-09-12): Cache credits and clear cache on generate calls.
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct GetSubscriptionResponse {
|
||||
/// Information about the subscription, if any.
|
||||
pub active_subscription: Option<ActiveSubscriptionInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ActiveSubscriptionInfo {
|
||||
pub subscription_token: UserSubscriptionToken,
|
||||
pub product_slug: ArtcraftSubscriptionSlug,
|
||||
pub namespace: PaymentsNamespace,
|
||||
|
||||
/// If the subscription is active, this is the next bill date
|
||||
pub next_bill_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// If the subscription is expired or is set to expire, this is the end date of the subscription.
|
||||
pub subscription_end_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl SerializeMarker for GetSubscriptionResponse {}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_get_subscription_command(
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> ResponseOrErrorMessage<GetSubscriptionResponse> {
|
||||
|
||||
info!("storyteller_get_subscription_command called");
|
||||
|
||||
let credits = get(
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error getting credits: {:?}", err);
|
||||
format!("Error getting credits: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok(credits.into())
|
||||
}
|
||||
|
||||
async fn get(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<GetSubscriptionResponse> {
|
||||
|
||||
let maybe_creds = storyteller_creds_manager.get_credentials()?;
|
||||
|
||||
let response = get_session_subscription(
|
||||
&app_env_configs.storyteller_host,
|
||||
maybe_creds.as_ref(),
|
||||
PaymentsNamespace::Artcraft,
|
||||
).await?;
|
||||
|
||||
Ok(GetSubscriptionResponse {
|
||||
active_subscription: response.active_subscription
|
||||
.map(|sub| {
|
||||
let product_slug = match ArtcraftSubscriptionSlug::from_str(&sub.product_slug) {
|
||||
Ok(product_slug) => product_slug,
|
||||
Err(err) => {
|
||||
error!("Received unknown product slug from storyteller: {:?}, error: {:?}", sub.product_slug, err);
|
||||
return None;
|
||||
},
|
||||
};
|
||||
Some(ActiveSubscriptionInfo {
|
||||
subscription_token: sub.subscription_token,
|
||||
product_slug,
|
||||
namespace: sub.namespace,
|
||||
next_bill_at: sub.next_bill_at,
|
||||
subscription_end_at: sub.subscription_end_at,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
})
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod storyteller_open_credits_purchase_command;
|
||||
pub mod storyteller_open_subscription_purchase_command;
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, OpenStorytellerBillingWindowArgs, BillingWindowCase};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct StorytellerOpenCreditsPurchaseCommand {
|
||||
pub credits_pack: Option<ArtcraftCreditsPackSlug>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_credits_purchase_command(
|
||||
app: AppHandle,
|
||||
request: StorytellerOpenCreditsPurchaseCommand,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_credits_purchase_command called");
|
||||
|
||||
let credits_pack = request.credits_pack.ok_or("Credits pack is required")?;
|
||||
|
||||
do_open_billing(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
credits_pack,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening credits purchase window: {:?}", err);
|
||||
format!("Error opening credits purchase window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_billing(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
credits_pack: ArtcraftCreditsPackSlug,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::CreditsPack {
|
||||
credits_pack,
|
||||
}
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, OpenStorytellerBillingWindowArgs, BillingWindowCase};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct StorytellerOpenSubscriptionPurchaseCommand {
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
pub cadence: Option<PlanBillingCadence>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_subscription_purchase_command(
|
||||
app: AppHandle,
|
||||
request: StorytellerOpenSubscriptionPurchaseCommand,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_subscription_purchase_command called");
|
||||
|
||||
let plan = request.plan.ok_or("Plan is required")?;
|
||||
let cadence = request.cadence.ok_or("Cadence is required")?;
|
||||
|
||||
do_open_billing(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
plan,
|
||||
cadence,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening subscription purchase window: {:?}", err);
|
||||
format!("Error opening subscription purchase window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_billing(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadence,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_env_configs,
|
||||
app_data_root,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::Subscription {
|
||||
plan,
|
||||
cadence,
|
||||
}
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
pub mod storyteller_open_customer_portal_cancel_plan_command;
|
||||
pub mod storyteller_open_customer_portal_manage_plan_command;
|
||||
pub mod storyteller_open_customer_portal_switch_plan_command;
|
||||
pub mod storyteller_open_customer_portal_update_payment_method_command;
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, BillingWindowCase, OpenStorytellerBillingWindowArgs};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_customer_portal_cancel_plan_command(
|
||||
app: AppHandle,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_customer_portal_cancel_plan_command called");
|
||||
|
||||
do_open_portal(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening customer portal window: {:?}", err);
|
||||
format!("Error opening customer portal window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_portal(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::CustomerPortalCancelPlan,
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, BillingWindowCase, OpenStorytellerBillingWindowArgs};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_customer_portal_manage_plan_command(
|
||||
app: AppHandle,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_customer_portal_manage_plan_command called");
|
||||
|
||||
do_open_portal(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening customer portal window: {:?}", err);
|
||||
format!("Error opening customer portal window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_portal(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::CustomerPortalManagePlan,
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, BillingWindowCase, OpenStorytellerBillingWindowArgs};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::PlanBillingCadenceConfirmation;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct StorytellerOpenCustomerPortalSwitchPlanCommand {
|
||||
pub plan: Option<ArtcraftSubscriptionSlug>,
|
||||
pub cadence: Option<PlanBillingCadenceConfirmation>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_customer_portal_switch_plan_command(
|
||||
app: AppHandle,
|
||||
request: StorytellerOpenCustomerPortalSwitchPlanCommand,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_customer_portal_switch_plan_command called");
|
||||
|
||||
let plan = request.plan.ok_or("Plan is required")?;
|
||||
let cadence = request.cadence.ok_or("Cadence is required")?;
|
||||
|
||||
do_open_portal(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
plan,
|
||||
cadence,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening customer portal window: {:?}", err);
|
||||
format!("Error opening customer portal window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_portal(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadenceConfirmation,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::CustomerPortalSwitchPlan {
|
||||
plan,
|
||||
cadence,
|
||||
}
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, BillingWindowCase, OpenStorytellerBillingWindowArgs};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::PlanBillingCadenceConfirmation;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::Deserialize;
|
||||
use tauri::{AppHandle, State};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_open_customer_portal_update_payment_method_command(
|
||||
app: AppHandle,
|
||||
app_data_root: State<'_, AppDataRoot>,
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> Result<String, String> {
|
||||
info!("storyteller_open_customer_portal_update_payment_method_command called");
|
||||
|
||||
do_open_portal(
|
||||
&app,
|
||||
&app_data_root,
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error opening customer portal window: {:?}", err);
|
||||
format!("Error opening customer portal window: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok("result".to_string())
|
||||
}
|
||||
|
||||
async fn do_open_portal(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<()> {
|
||||
info!("Building billing window...");
|
||||
|
||||
open_storyteller_billing_window(OpenStorytellerBillingWindowArgs {
|
||||
app,
|
||||
app_data_root,
|
||||
app_env_configs,
|
||||
storyteller_creds_manager,
|
||||
billing_window_case: BillingWindowCase::CustomerPortalUpdatePaymentMethod,
|
||||
}).await?;
|
||||
|
||||
info!("Done.");
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
pub mod state;
|
||||
pub mod threads;
|
||||
pub mod windows;
|
||||
pub mod commands;
|
||||
pub mod threads;
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod open_storyteller_billing_window;
|
||||
pub mod storyteller_billing_window_thread;
|
||||
-298
@@ -1,298 +0,0 @@
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::services::sora::windows::sora_login_window::sora_login_thread::sora_login_thread;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::storyteller_billing_window_thread::storyteller_billing_window_thread;
|
||||
use anyhow::anyhow;
|
||||
use artcraft_api_defs::stripe_artcraft::create_credits_pack_checkout::StripeArtcraftCreateCreditsPackCheckoutRequest;
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::{PlanBillingCadence, StripeArtcraftCreateSubscriptionCheckoutRequest};
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_cancel_plan::StripeArtcraftCustomerPortalCancelPlanRequest;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_manage_plan::StripeArtcraftCustomerPortalManagePlanRequest;
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_switch_plan::{PlanBillingCadenceConfirmation, StripeArtcraftCustomerPortalSwitchPlanRequest};
|
||||
use artcraft_api_defs::stripe_artcraft::customer_portal_update_payment_method::StripeArtcraftCustomerPortalUpdatePaymentMethodRequest;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use errors::AnyhowResult;
|
||||
use log::info;
|
||||
use reqwest::Url;
|
||||
use storyteller_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use storyteller_client::stripe_artcraft::create_credits_pack_checkout::create_credits_pack_checkout;
|
||||
use storyteller_client::stripe_artcraft::create_subscription_checkout::create_subscription_checkout;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_cancel_plan::customer_portal_cancel_plan;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_manage_plan::customer_portal_manage_plan;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_switch_plan::customer_portal_switch_plan;
|
||||
use storyteller_client::stripe_artcraft::customer_portal_update_payment_method::customer_portal_update_payment_method;
|
||||
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
|
||||
|
||||
pub const BILLING_WINDOW_NAME: &str = "artcraft_billing_window";
|
||||
|
||||
pub struct OpenStorytellerBillingWindowArgs<'a> {
|
||||
pub app: &'a AppHandle,
|
||||
pub app_env_configs: &'a AppEnvConfigs,
|
||||
pub app_data_root: &'a AppDataRoot,
|
||||
pub storyteller_creds_manager: &'a StorytellerCredentialManager,
|
||||
pub billing_window_case: BillingWindowCase,
|
||||
}
|
||||
|
||||
pub enum BillingWindowCase {
|
||||
/// Purchase a subscription plan with the billing cadence supplied.
|
||||
Subscription {
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadence,
|
||||
},
|
||||
|
||||
/// Purchase a credits pack.
|
||||
CreditsPack {
|
||||
credits_pack: ArtcraftCreditsPackSlug,
|
||||
},
|
||||
|
||||
/// Cancel the user's subscription.
|
||||
/// Stripe shows a confirmation and then cancels the subscription.
|
||||
CustomerPortalCancelPlan,
|
||||
|
||||
/// Manage the user's subscription.
|
||||
/// Stripe shows complete controls for updating the plan, payment method, etc.
|
||||
CustomerPortalManagePlan,
|
||||
|
||||
/// Switch the user's subscription plan.
|
||||
/// Stripe pre-fills to the selected plan and cadence.
|
||||
CustomerPortalSwitchPlan {
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadenceConfirmation,
|
||||
},
|
||||
|
||||
/// Change the payment method attached to the customer / subscription.
|
||||
CustomerPortalUpdatePaymentMethod,
|
||||
}
|
||||
|
||||
pub async fn open_storyteller_billing_window(
|
||||
args: OpenStorytellerBillingWindowArgs<'_>,
|
||||
) -> AnyhowResult<()> {
|
||||
|
||||
if args.app.get_window(BILLING_WINDOW_NAME).is_some() {
|
||||
return Err(anyhow!("Billing window already open"));
|
||||
}
|
||||
|
||||
info!("Building Stripe Checkout session...");
|
||||
|
||||
let creds = args.storyteller_creds_manager.get_credentials_required()?;
|
||||
|
||||
let checkout_url = match args.billing_window_case {
|
||||
BillingWindowCase::Subscription { plan, cadence } => {
|
||||
get_subscription_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
plan,
|
||||
cadence,
|
||||
).await?
|
||||
},
|
||||
BillingWindowCase::CreditsPack { credits_pack } => {
|
||||
get_credits_pack_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
credits_pack,
|
||||
).await?
|
||||
},
|
||||
BillingWindowCase::CustomerPortalCancelPlan => {
|
||||
get_customer_portal_cancel_plan_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
).await?
|
||||
},
|
||||
BillingWindowCase::CustomerPortalManagePlan => {
|
||||
get_customer_portal_manage_plan_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
).await?
|
||||
},
|
||||
BillingWindowCase::CustomerPortalSwitchPlan { plan, cadence } => {
|
||||
get_customer_portal_switch_plan_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
plan,
|
||||
cadence,
|
||||
).await?
|
||||
},
|
||||
BillingWindowCase::CustomerPortalUpdatePaymentMethod => {
|
||||
get_customer_portal_update_payment_method_url(
|
||||
args.app_env_configs,
|
||||
&creds,
|
||||
).await?
|
||||
}
|
||||
};
|
||||
|
||||
do_open_window(args.app, args.app_data_root, checkout_url).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_subscription_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadence,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting URL for subscription checkout...");
|
||||
|
||||
let request = StripeArtcraftCreateSubscriptionCheckoutRequest {
|
||||
plan: Some(plan),
|
||||
cadence: Some(cadence),
|
||||
};
|
||||
|
||||
let result = create_subscription_checkout(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Checkout session created...");
|
||||
|
||||
let url = Url::parse(&result.stripe_checkout_redirect_url)?;
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
async fn get_credits_pack_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
credits_pack: ArtcraftCreditsPackSlug,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting URL for credits pack checkout...");
|
||||
|
||||
let request = StripeArtcraftCreateCreditsPackCheckoutRequest {
|
||||
credits_pack: Some(credits_pack),
|
||||
};
|
||||
|
||||
let result = create_credits_pack_checkout(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Credits pack checkout session created...");
|
||||
Ok(Url::parse(&result.stripe_checkout_redirect_url)?)
|
||||
}
|
||||
|
||||
async fn get_customer_portal_cancel_plan_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting customer portal cancel plan session...");
|
||||
|
||||
let request = StripeArtcraftCustomerPortalCancelPlanRequest {
|
||||
portal_config_id: None,
|
||||
};
|
||||
|
||||
let result = customer_portal_cancel_plan(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Customer portal cancel plan session created...");
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
async fn get_customer_portal_manage_plan_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting customer portal manage plan session...");
|
||||
|
||||
let request = StripeArtcraftCustomerPortalManagePlanRequest {
|
||||
portal_config_id: None,
|
||||
};
|
||||
|
||||
let result = customer_portal_manage_plan(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Customer portal manage plan session created...");
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
async fn get_customer_portal_switch_plan_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
plan: ArtcraftSubscriptionSlug,
|
||||
cadence: PlanBillingCadenceConfirmation,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting customer portal switch plan session...");
|
||||
|
||||
let request = StripeArtcraftCustomerPortalSwitchPlanRequest {
|
||||
portal_config_id: None,
|
||||
plan: Some(plan),
|
||||
cadence: Some(cadence),
|
||||
};
|
||||
|
||||
let result = customer_portal_switch_plan(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Customer portal switch plan session created...");
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
async fn get_customer_portal_update_payment_method_url(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds: &StorytellerCredentialSet,
|
||||
) -> AnyhowResult<Url> {
|
||||
|
||||
info!("Getting customer portal update payment method session...");
|
||||
|
||||
let request = StripeArtcraftCustomerPortalUpdatePaymentMethodRequest {
|
||||
portal_config_id: None,
|
||||
};
|
||||
|
||||
let result = customer_portal_update_payment_method(
|
||||
&app_env_configs.storyteller_host,
|
||||
Some(&storyteller_creds),
|
||||
request,
|
||||
).await?;
|
||||
|
||||
info!("Customer portal update payment method created...");
|
||||
Ok(Url::parse(&result.stripe_portal_url)?)
|
||||
}
|
||||
|
||||
async fn do_open_window(
|
||||
app: &AppHandle,
|
||||
app_data_root: &AppDataRoot,
|
||||
url: Url,
|
||||
) -> AnyhowResult<()> {
|
||||
let url = WebviewUrl::External(url);
|
||||
|
||||
info!("Opening checkout window...");
|
||||
|
||||
// TODO(bt,2025-09-01): We probably need to open this in a real browser to get autocomplete.
|
||||
let _window = WebviewWindowBuilder::new(app, BILLING_WINDOW_NAME, url)
|
||||
.always_on_top(false)
|
||||
.title("ArtCraft Billing")
|
||||
.center()
|
||||
.resizable(true)
|
||||
.visible(true)
|
||||
.closable(true)
|
||||
.min_inner_size(1000.0, 800.0)
|
||||
.focused(true)
|
||||
.devtools(true)
|
||||
.build()?;
|
||||
|
||||
//let _webview = window.get_webview(BILLING_WINDOW_NAME)
|
||||
// .ok_or_else(|| anyhow!("no webview found"))?;
|
||||
|
||||
let app_handle = app.clone();
|
||||
let app_data_root = app_data_root.clone();
|
||||
|
||||
let _ = tauri::async_runtime::spawn(async move {
|
||||
storyteller_billing_window_thread(app_handle, app_data_root).await;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::credits_balance_changed_event::CreditsBalanceChangedEvent;
|
||||
use crate::core::events::functional_events::refresh_account_state_event::RefreshAccountStateEvent;
|
||||
use crate::core::events::functional_events::subscription_plan_changed_event::SubscriptionPlanChangedEvent;
|
||||
use crate::core::events::generation_events::common::{GenerationAction, GenerationServiceProvider};
|
||||
use crate::core::events::generation_events::generation_complete_event::GenerationCompleteEvent;
|
||||
use crate::core::events::sendable_event_trait::SendableEvent;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::utils::window::get_webview_window_hostname::get_webview_window_hostname;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::state::midjourney_user_info::MidjourneyUserInfo;
|
||||
use crate::services::midjourney::windows::extract_midjourney_webview_cookies::extract_midjourney_webview_cookies;
|
||||
use crate::services::sora::events::sora_login_success_event::SoraLoginSuccessEvent;
|
||||
use crate::services::sora::state::sora_credential_manager::SoraCredentialManager;
|
||||
use crate::services::sora::windows::sora_login_window::extract_sora_webview_cookies::extract_sora_webview_cookies;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::BILLING_WINDOW_NAME;
|
||||
use anyhow::anyhow;
|
||||
use cookie_store::cookie_store::CookieStore;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use midjourney_client::client::midjourney_hostname::MidjourneyHostname;
|
||||
use midjourney_client::credentials::cookie_store_has_auth_cookies::cookie_store_has_auth_cookies;
|
||||
use midjourney_client::recipes::get_user_info::{get_user_info, GetUserInfoRequest};
|
||||
use openai_sora_client::creds::sora_credential_set::SoraCredentialSet;
|
||||
use openai_sora_client::recipes::maybe_upgrade_or_renew_session::maybe_upgrade_or_renew_session;
|
||||
use openai_sora_client::utils::has_session_cookie::{has_session_cookie, SessionCookiePresence};
|
||||
use tauri::{AppHandle, Manager, WebviewWindow};
|
||||
|
||||
pub async fn storyteller_billing_window_thread(
|
||||
app: AppHandle,
|
||||
app_data_root: AppDataRoot,
|
||||
) {
|
||||
loop {
|
||||
let billing_webview_window = match app.get_webview_window(BILLING_WINDOW_NAME) {
|
||||
Some(webview) => webview,
|
||||
None => {
|
||||
info!("Exit billing window thread.");
|
||||
return; // NB: Only exit if we don't have the webview.
|
||||
}
|
||||
};
|
||||
|
||||
let result = check_billing_window(
|
||||
&app,
|
||||
&billing_webview_window,
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Err(err) => {
|
||||
error!("Error checking billing window: {:?}", err);
|
||||
}
|
||||
Ok(false) => {} // Continue iteration and try again...
|
||||
Ok(true) => {
|
||||
info!("Checkout complete; exiting");
|
||||
if let Err(err) = billing_webview_window.close() {
|
||||
error!("Error closing billing window: {:?}", err);
|
||||
}
|
||||
|
||||
// TODO: We can distinguish between subscription and credits impacts if we send a parameter
|
||||
// from the webview. For now, just refresh everything.
|
||||
|
||||
// Refresh the credits view
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
SubscriptionPlanChangedEvent{}.send_infallible(&app);
|
||||
|
||||
// And in case there's a race condition (likely), do it again after a delay.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(5_000)).await;
|
||||
CreditsBalanceChangedEvent{}.send_infallible(&app);
|
||||
SubscriptionPlanChangedEvent{}.send_infallible(&app);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(1_000)).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if we can exit.
|
||||
async fn check_billing_window(
|
||||
app_handle: &AppHandle,
|
||||
webview_window: &WebviewWindow,
|
||||
) -> AnyhowResult<bool> {
|
||||
|
||||
let url = webview_window.url()?;
|
||||
|
||||
let hostname= url
|
||||
.host()
|
||||
.ok_or(anyhow!("no host in url"))?
|
||||
.to_string();
|
||||
|
||||
match hostname.as_str() {
|
||||
"getartcraft.com" |
|
||||
"storyteller.ai" => {
|
||||
// Checkout done. Fall-through.
|
||||
},
|
||||
"stripe.com" | "checkout.stripe.com" => {
|
||||
return Ok(false) // Still in checkout flow.
|
||||
}
|
||||
_ => {
|
||||
return Ok(false) // Unknown hostname...
|
||||
}
|
||||
}
|
||||
|
||||
let path = url
|
||||
.path()
|
||||
.to_string();
|
||||
|
||||
// TODO: This is brittle.
|
||||
|
||||
let success = path.contains("checkout_success");
|
||||
|
||||
info!("Checkout success: {}", success);
|
||||
|
||||
// TODO: Send success events:
|
||||
|
||||
// let event = RefreshAccountStateEvent {
|
||||
// provider: Some(GenerationProvider::Midjourney),
|
||||
// };
|
||||
//
|
||||
// if let Err(err) = event.send(&app_handle) {
|
||||
// error!("Failed to send RefreshAccountStateEvent: {:?}", err); // Fail open
|
||||
// }
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -36,7 +36,6 @@
|
||||
"http://ipc.localhost",
|
||||
"https://*.fakeyou.com",
|
||||
"https://*.storyteller.ai",
|
||||
"https://*.clarity.ms",
|
||||
"https://unpkg.com/detect-gpu@5.0.70/",
|
||||
"https://www.google-analytics.com",
|
||||
"https://www.googletagmanager.com",
|
||||
@@ -47,10 +46,9 @@
|
||||
"img-src": "* data: blob: 'unsafe-inline'",
|
||||
"script-src": [
|
||||
"'self'",
|
||||
"https://*.clarity.ms",
|
||||
"https://*.fakeyou.com",
|
||||
"https://*.storyteller.ai",
|
||||
"https://clarity.ms",
|
||||
"https://www.clarity.ms",
|
||||
"ipc://localhost",
|
||||
"tauri://localhost"
|
||||
],
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"http://ipc.localhost",
|
||||
"https://*.fakeyou.com",
|
||||
"https://*.storyteller.ai",
|
||||
"https://*.clarity.ms",
|
||||
"https://unpkg.com/detect-gpu@5.0.70/",
|
||||
"https://www.google-analytics.com",
|
||||
"https://www.googletagmanager.com",
|
||||
@@ -47,10 +46,9 @@
|
||||
"img-src": "* data: blob: 'unsafe-inline'",
|
||||
"script-src": [
|
||||
"'self'",
|
||||
"https://*.clarity.ms",
|
||||
"https://*.fakeyou.com",
|
||||
"https://*.storyteller.ai",
|
||||
"https://clarity.ms",
|
||||
"https://www.clarity.ms",
|
||||
"ipc://localhost",
|
||||
"tauri://localhost"
|
||||
],
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
|
||||
// TODO(bt,2024-10-01): This is only used for inserts into the idempotency table. We should move or rename it.
|
||||
#[derive(Debug)]
|
||||
#[deprecated(note="This is an old error type.")]
|
||||
pub enum DatabaseQueryError {
|
||||
/// A duplicate idempotency token error occurred.
|
||||
/// This should be surfaced as a 400 to the user.
|
||||
#[deprecated(note="This is an old error type.")]
|
||||
IdempotencyDuplicateKeyError,
|
||||
|
||||
/// An uncategorized error occurred.
|
||||
/// This will likely result in a 500 for the user.
|
||||
#[deprecated(note="This is an old error type.")]
|
||||
SqlxError(sqlx::Error),
|
||||
|
||||
/// An uncategorized non-database error occurred.
|
||||
/// This will likely result in a 500 for the user.
|
||||
#[deprecated(note="This is an old error type.")]
|
||||
AnyhowError(anyhow::Error),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
pub mod database_insert_error;
|
||||
pub mod database_query_error;
|
||||
pub mod select_exactly_one_error;
|
||||
pub mod select_optional_record_error;
|
||||
pub mod database_query_error;
|
||||
@@ -1,29 +0,0 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SelectExactlyOneError {
|
||||
NotFound,
|
||||
DatabaseError(sqlx::Error),
|
||||
}
|
||||
|
||||
impl Error for SelectExactlyOneError {}
|
||||
|
||||
impl Display for SelectExactlyOneError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SelectExactlyOneError::NotFound => write!(f, "Record not found"),
|
||||
SelectExactlyOneError::DatabaseError(e) => write!(f, "Database error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for SelectExactlyOneError {
|
||||
fn from(e: sqlx::Error) -> Self {
|
||||
match e {
|
||||
sqlx::Error::RowNotFound => return SelectExactlyOneError::NotFound,
|
||||
_ => {} // Fall-through
|
||||
}
|
||||
SelectExactlyOneError::DatabaseError(e)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SelectOptionalRecordError {
|
||||
DatabaseError(sqlx::Error),
|
||||
|
||||
/// Manually constructed error from mapping raw results into a final struct
|
||||
/// Specifically, this results from manually mapping an Option<T> to T, where T should have been non-nullable,
|
||||
/// but sqlx couldn't enforce that at compile time.
|
||||
RequiredFieldWasNull(&'static str),
|
||||
}
|
||||
|
||||
impl Error for SelectOptionalRecordError {}
|
||||
|
||||
impl Display for SelectOptionalRecordError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SelectOptionalRecordError::DatabaseError(e) => write!(f, "Database error: {}", e),
|
||||
SelectOptionalRecordError::RequiredFieldWasNull(field_name) => write!(f, "Required field was null: {}", field_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for SelectOptionalRecordError {
|
||||
fn from(e: sqlx::Error) -> Self {
|
||||
SelectOptionalRecordError::DatabaseError(e)
|
||||
}
|
||||
}
|
||||
@@ -12,5 +12,4 @@ pub mod helpers;
|
||||
pub mod mediators;
|
||||
pub mod payloads;
|
||||
pub mod queries;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
+3
-11
@@ -1,7 +1,7 @@
|
||||
use anyhow::anyhow;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
|
||||
use crate::helpers::boolean_converters::i8_to_bool;
|
||||
|
||||
@@ -17,14 +17,6 @@ pub async fn get_stripe_webhook_event_log_by_id(
|
||||
stripe_event_id: &str,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> AnyhowResult<Option<StripeWebhookEventLog>> {
|
||||
let mut connection = mysql_pool.acquire().await?;
|
||||
get_stripe_webhook_event_log_by_id_with_connection(stripe_event_id, &mut connection).await
|
||||
}
|
||||
|
||||
pub async fn get_stripe_webhook_event_log_by_id_with_connection(
|
||||
stripe_event_id: &str,
|
||||
mysql_connection: &mut PoolConnection<MySql>
|
||||
) -> AnyhowResult<Option<StripeWebhookEventLog>> {
|
||||
|
||||
let maybe_record = sqlx::query_as!(
|
||||
RawStripeWebhookEventLogFromDb,
|
||||
@@ -38,7 +30,7 @@ WHERE
|
||||
"#,
|
||||
stripe_event_id,
|
||||
)
|
||||
.fetch_one(&mut **mysql_connection)
|
||||
.fetch_one(mysql_pool)
|
||||
.await;
|
||||
|
||||
match maybe_record {
|
||||
|
||||
+13
-28
@@ -1,9 +1,8 @@
|
||||
use anyhow::anyhow;
|
||||
use chrono::NaiveDateTime;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
use sqlx::mysql::MySqlArguments;
|
||||
use sqlx::query::Query;
|
||||
use sqlx::{MySql, MySqlPool, Transaction};
|
||||
|
||||
// TODO: Make a trait with default impls to handle common query concerns.
|
||||
|
||||
@@ -22,31 +21,8 @@ pub struct InsertStripeWebhookEventLog {
|
||||
impl InsertStripeWebhookEventLog {
|
||||
|
||||
pub async fn insert(&self, mysql_pool: &MySqlPool) -> AnyhowResult<()> {
|
||||
let query_result = self.query()
|
||||
.execute(mysql_pool).await;
|
||||
|
||||
let _record_id = match query_result {
|
||||
Ok(res) => res.last_insert_id(),
|
||||
Err(err) => return Err(anyhow!("Error creating stripe webhook event log: {:?}", err)),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_transactional(&self, transaction: &mut Transaction<'_, MySql>) -> AnyhowResult<()> {
|
||||
let query_result = self.query()
|
||||
.execute(&mut **transaction).await;
|
||||
|
||||
let _record_id = match query_result {
|
||||
Ok(res) => res.last_insert_id(),
|
||||
Err(err) => return Err(anyhow!("Error creating stripe webhook event log: {:?}", err)),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query(&self) -> Query<MySql, MySqlArguments> {
|
||||
sqlx::query!(
|
||||
let query = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO stripe_webhook_event_logs
|
||||
SET
|
||||
@@ -69,6 +45,15 @@ SET
|
||||
&self.maybe_user_token,
|
||||
self.action_was_taken,
|
||||
self.should_ignore_retry,
|
||||
)
|
||||
);
|
||||
|
||||
let query_result = query.execute(mysql_pool).await;
|
||||
|
||||
let _record_id = match query_result {
|
||||
Ok(res) => res.last_insert_id(),
|
||||
Err(err) => return Err(anyhow!("Error creating stripe webhook event log: {:?}", err)),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,9 @@ pub mod tags;
|
||||
pub mod trending_model_analytics;
|
||||
pub mod tts;
|
||||
pub mod twitch;
|
||||
pub mod unsubscribe_reason;
|
||||
pub mod users;
|
||||
pub mod vocoder;
|
||||
pub mod voice_clone_requests;
|
||||
pub mod voice_conversion;
|
||||
pub mod voice_designer;
|
||||
pub mod w2l;
|
||||
pub mod wallets;
|
||||
mod wallet_ledger_entries;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub mod insert_unsubscribe_reason;
|
||||
@@ -6,5 +6,4 @@ pub mod user_profiles;
|
||||
pub mod user_ratings;
|
||||
pub mod user_roles;
|
||||
pub mod user_sessions;
|
||||
pub mod user_stripe_customer_links;
|
||||
pub mod user_subscriptions;
|
||||
|
||||
+3
-16
@@ -1,6 +1,6 @@
|
||||
use anyhow::anyhow;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use errors::AnyhowResult;
|
||||
|
||||
pub async fn update_user_record_with_new_stripe_customer_id(
|
||||
@@ -8,19 +8,6 @@ pub async fn update_user_record_with_new_stripe_customer_id(
|
||||
user_token: &str,
|
||||
maybe_stripe_customer_id: Option<&str>
|
||||
) -> AnyhowResult<()> {
|
||||
let mut mysql_connection = mysql_pool.acquire().await?;
|
||||
update_user_record_with_new_stripe_customer_id_with_connection(
|
||||
&mut mysql_connection,
|
||||
user_token,
|
||||
maybe_stripe_customer_id
|
||||
).await
|
||||
}
|
||||
|
||||
pub async fn update_user_record_with_new_stripe_customer_id_with_connection(
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
user_token: &str,
|
||||
maybe_stripe_customer_id: Option<&str>
|
||||
) -> AnyhowResult<()> {
|
||||
|
||||
// TODO: This will overwrite whatever the previous customer_id was.
|
||||
// Should we guard against that?
|
||||
@@ -39,7 +26,7 @@ LIMIT 1
|
||||
user_token,
|
||||
);
|
||||
|
||||
let query_result = query.execute(&mut ** mysql_connection).await;
|
||||
let query_result = query.execute(mysql_pool).await;
|
||||
|
||||
match query_result {
|
||||
Ok(_) => Ok(()),
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
use crate::errors::select_optional_record_error::SelectOptionalRecordError;
|
||||
use crate::types::query_map::QueryMap;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use sqlx;
|
||||
use sqlx::mysql::MySqlRow;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::MySql;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
// NB: We only store one stripe customer ID per user per namespace.
|
||||
// And each stripe customer ID only exists in the database once.
|
||||
// If users need a new stripe customer ID, we need to hard delete the existing record.
|
||||
// This should not be used for critical functions, but rather convenience of
|
||||
// linking customer records.
|
||||
pub struct UserStripeCustomerLink {
|
||||
pub user_token: UserToken,
|
||||
|
||||
pub payments_namespace: PaymentsNamespace,
|
||||
|
||||
pub stripe_customer_id: String,
|
||||
}
|
||||
|
||||
pub async fn find_user_stripe_customer_link_using_connection(
|
||||
user_token: &UserToken,
|
||||
namespace: PaymentsNamespace,
|
||||
connection: &mut PoolConnection<MySql>,
|
||||
) -> Result<Option<UserStripeCustomerLink>, SelectOptionalRecordError> {
|
||||
|
||||
let query = query(user_token, namespace);
|
||||
|
||||
let result = query
|
||||
.fetch_optional(&mut **connection)
|
||||
.await;
|
||||
|
||||
map_result(result)
|
||||
}
|
||||
|
||||
|
||||
pub async fn find_user_stripe_customer_link_using_transaction(
|
||||
user_token: &UserToken,
|
||||
namespace: PaymentsNamespace,
|
||||
transaction: &mut sqlx::Transaction<'_, MySql>,
|
||||
) -> Result<Option<UserStripeCustomerLink>, SelectOptionalRecordError> {
|
||||
|
||||
let query = query(user_token, namespace);
|
||||
|
||||
let result = query
|
||||
.fetch_optional(&mut **transaction)
|
||||
.await;
|
||||
|
||||
map_result(result)
|
||||
}
|
||||
|
||||
fn map_result(result: Result<Option<RawUserStripeCustomerLink>, sqlx::Error>) -> Result<Option<UserStripeCustomerLink>, SelectOptionalRecordError> {
|
||||
match result {
|
||||
Ok(Some(record)) => Ok(Some(UserStripeCustomerLink {
|
||||
user_token: record.user_token,
|
||||
payments_namespace: record.payments_namespace,
|
||||
stripe_customer_id: record.stripe_customer_id,
|
||||
})),
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn query(user_token: &UserToken, namespace: PaymentsNamespace)
|
||||
-> QueryMap<impl Send + FnMut(MySqlRow) -> Result<RawUserStripeCustomerLink, sqlx::Error>>
|
||||
{
|
||||
sqlx::query_as!(
|
||||
RawUserStripeCustomerLink,
|
||||
r#"
|
||||
SELECT
|
||||
user_token as `user_token: tokens::tokens::users::UserToken`,
|
||||
payments_namespace as `payments_namespace: enums::common::payments_namespace::PaymentsNamespace`,
|
||||
stripe_customer_id
|
||||
|
||||
FROM user_stripe_customer_links
|
||||
|
||||
WHERE
|
||||
user_token = ?
|
||||
AND payments_namespace = ?
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
user_token.as_str(),
|
||||
namespace.to_str(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct RawUserStripeCustomerLink {
|
||||
user_token: UserToken,
|
||||
payments_namespace: PaymentsNamespace,
|
||||
stripe_customer_id: String,
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user