diff --git a/crates/app/src/assets.rs b/crates/app/src/assets.rs
index 092889f..c8db3be 100644
--- a/crates/app/src/assets.rs
+++ b/crates/app/src/assets.rs
@@ -55,7 +55,6 @@ pub const AUTH_BASE: &str = include_str!("./public/html/auth/base.lisp");
pub const AUTH_LOGIN: &str = include_str!("./public/html/auth/login.lisp");
pub const AUTH_REGISTER: &str = include_str!("./public/html/auth/register.lisp");
pub const AUTH_CONNECTION: &str = include_str!("./public/html/auth/connection.lisp");
-pub const AUTH_SELLER_CONNECTION: &str = include_str!("./public/html/auth/seller_connection.lisp");
pub const PROFILE_BASE: &str = include_str!("./public/html/profile/base.lisp");
pub const PROFILE_POSTS: &str = include_str!("./public/html/profile/posts.lisp");
@@ -143,8 +142,6 @@ pub const LITTLEWEB_SERVICE: &str = include_str!("./public/html/littleweb/servic
pub const LITTLEWEB_DOMAIN: &str = include_str!("./public/html/littleweb/domain.lisp");
pub const LITTLEWEB_BROWSER: &str = include_str!("./public/html/littleweb/browser.lisp");
-pub const MARKETPLACE_SELLER: &str = include_str!("./public/html/marketplace/seller.lisp");
-
pub const MAIL_RECEIVED: &str = include_str!("./public/html/mail/received.lisp");
pub const MAIL_SENT: &str = include_str!("./public/html/mail/sent.lisp");
pub const MAIL_COMPOSE: &str = include_str!("./public/html/mail/compose.lisp");
@@ -297,7 +294,6 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD {
write_template!(html_path->"auth/login.html"(crate::assets::AUTH_LOGIN) --config=config --lisp plugins);
write_template!(html_path->"auth/register.html"(crate::assets::AUTH_REGISTER) --config=config --lisp plugins);
write_template!(html_path->"auth/connection.html"(crate::assets::AUTH_CONNECTION) --config=config --lisp plugins);
- write_template!(html_path->"auth/seller_connection.html"(crate::assets::AUTH_SELLER_CONNECTION) --config=config --lisp plugins);
write_template!(html_path->"profile/base.html"(crate::assets::PROFILE_BASE) -d "profile" --config=config --lisp plugins);
write_template!(html_path->"profile/posts.html"(crate::assets::PROFILE_POSTS) --config=config --lisp plugins);
@@ -378,8 +374,6 @@ pub(crate) async fn write_assets(config: &Config) -> PathBufD {
write_template!(html_path->"littleweb/domain.html"(crate::assets::LITTLEWEB_DOMAIN) --config=config --lisp plugins);
write_template!(html_path->"littleweb/browser.html"(crate::assets::LITTLEWEB_BROWSER) --config=config --lisp plugins);
- write_template!(html_path->"marketplace/seller.html"(crate::assets::MARKETPLACE_SELLER) -d "marketplace" --config=config --lisp plugins);
-
write_template!(html_path->"mail/received.html"(crate::assets::MAIL_RECEIVED) -d "mail" --config=config --lisp plugins);
write_template!(html_path->"mail/sent.html"(crate::assets::MAIL_SENT) --config=config --lisp plugins);
write_template!(html_path->"mail/compose.html"(crate::assets::MAIL_COMPOSE) --config=config --lisp plugins);
diff --git a/crates/app/src/langs/en-US.toml b/crates/app/src/langs/en-US.toml
index f995deb..523d7f7 100644
--- a/crates/app/src/langs/en-US.toml
+++ b/crates/app/src/langs/en-US.toml
@@ -316,12 +316,6 @@ version = "1.0.0"
"littleweb:action.rename" = "Rename"
"littleweb:action.add" = "Add"
-"marketplace:label.products" = "Products"
-"marketplace:label.status" = "Status"
-"marketplace:action.get_started" = "Get started"
-"marketplace:action.finsh_setting_up_account" = "Finish setting up my account"
-"marketplace:action.open_seller_dashboard" = "Open seller dashboard"
-
"mail:label.received" = "Received"
"mail:label.sent" = "Sent"
"mail:label.compose" = "Compose"
diff --git a/crates/app/src/public/html/auth/base.lisp b/crates/app/src/public/html/auth/base.lisp
index 4940089..e9a8b9b 100644
--- a/crates/app/src/public/html/auth/base.lisp
+++ b/crates/app/src/public/html/auth/base.lisp
@@ -3,7 +3,7 @@
("class" "flex flex_col gap_2")
("style" "max-width: 48ch")
(h2
- ("class" "w_full text-center")
+ ("class" "w_full text_center")
; block for title
(text "{% block title %}{% endblock %}"))
(div
diff --git a/crates/app/src/public/html/auth/login.lisp b/crates/app/src/public/html/auth/login.lisp
index 9c1e2e4..c2e3cf0 100644
--- a/crates/app/src/public/html/auth/login.lisp
+++ b/crates/app/src/public/html/auth/login.lisp
@@ -113,10 +113,9 @@
(text "{% endblock %} {% block footer %}")
(span
- ("class" "small w_full text-center")
+ ("class" "small w_full text_center")
(text "Or, ")
(a
("href" "/auth/register")
(text "register")))
-
(text "{% endblock %}")
diff --git a/crates/app/src/public/html/auth/register.lisp b/crates/app/src/public/html/auth/register.lisp
index 4beb2fb..0fd9ae9 100644
--- a/crates/app/src/public/html/auth/register.lisp
+++ b/crates/app/src/public/html/auth/register.lisp
@@ -170,10 +170,9 @@
(text "{% endblock %} {% block footer %}")
(span
- ("class" "small w_full text-center")
+ ("class" "small w_full text_center")
(text "Or, ")
(a
("href" "/auth/login")
(text "login")))
-
(text "{% endblock %}")
diff --git a/crates/app/src/public/html/auth/seller_connection.lisp b/crates/app/src/public/html/auth/seller_connection.lisp
deleted file mode 100644
index c13b498..0000000
--- a/crates/app/src/public/html/auth/seller_connection.lisp
+++ /dev/null
@@ -1,25 +0,0 @@
-(text "{% extends \"auth/base.html\" %} {% block head %}")
-(title
- (text "Connection"))
-
-(text "{% endblock %} {% block title %}Connection{% endblock %} {% block content %}")
-(div
- ("class" "w_full flex_col gap_2")
- ("id" "status")
- (b
- (text "Working...")))
-
-(text "{% if connection_type == \"refresh\" %}")
-(script
- ("defer" "true")
- (text "setTimeout(async () => {
- trigger(\"seller::onboarding\");
- }, 1000);"))
-(text "{% elif connection_type == \"return\" %}")
-(script
- ("defer" "true")
- (text "setTimeout(async () => {
- document.getElementById(\"status\").innerHTML =
- `Account updated. You can now close this tab.`;
- }, 1000);"))
-(text "{%- endif %} {% endblock %}")
diff --git a/crates/app/src/public/html/components.lisp b/crates/app/src/public/html/components.lisp
index d2b2bf6..3870cc4 100644
--- a/crates/app/src/public/html/components.lisp
+++ b/crates/app/src/public/html/components.lisp
@@ -2668,7 +2668,7 @@
(td
(a
("href" "/community/{{ community.title }}/topic/{{ post.topic }}")
- ("class" "flex gap_1 items_center")
+ ("class" "flex gap_1 items_center w_content")
(text "{{ self::community_avatar(id=post.community, community=community) }}")
(span
(text "{% if community.context.display_name -%} {{ community.context.display_name }} {% else %} {{ community.title }} {%- endif %}"))))
diff --git a/crates/app/src/public/html/macros.lisp b/crates/app/src/public/html/macros.lisp
index 6efc6eb..cd35481 100644
--- a/crates/app/src/public/html/macros.lisp
+++ b/crates/app/src/public/html/macros.lisp
@@ -376,17 +376,3 @@
(span
(text "{{ text \"settings:tab.connections\" }}")))
(text "{%- endmacro %}")
-
-(text "{% macro seller_settings_nav_options() -%}")
-(a
- ("data-tab-button" "account")
- ("class" "active")
- ("href" "#/account")
- (icon (text "smile"))
- (span (str (text "settings:tab.account"))))
-(a
- ("data-tab-button" "products")
- ("href" "#/products")
- (icon (text "package"))
- (span (str (text "marketplace:label.products"))))
-(text "{%- endmacro %}")
diff --git a/crates/app/src/public/html/marketplace/seller.lisp b/crates/app/src/public/html/marketplace/seller.lisp
deleted file mode 100644
index c16bb70..0000000
--- a/crates/app/src/public/html/marketplace/seller.lisp
+++ /dev/null
@@ -1,79 +0,0 @@
-(text "{% extends \"root.html\" %} {% block head %}")
-(title
- (text "Seller settings - {{ config.name }}"))
-(text "{% endblock %} {% block body %} {{ macros::nav() }}")
-(main
- ("class" "flex flex_col gap_2")
-
- ; nav
- (div
- ("class" "mobile_nav mobile")
- ; primary nav
- (div
- ("class" "dropdown")
- ("style" "width: max-content")
- (button
- ("class" "raised small")
- ("onclick" "trigger('atto::hooks::dropdown', [event])")
- ("exclude" "dropdown")
- (icon (text "sliders-horizontal"))
- (span ("class" "current_tab_text") (text "account")))
- (div
- ("class" "inner left")
- (text "{{ macros::seller_settings_nav_options() }}"))))
-
- ; nav desktop
- (div
- ("class" "desktop pillmenu")
- (text "{{ macros::seller_settings_nav_options() }}"))
-
- ; ...
- (div
- ("class" "card w_full lowered flex flex_col gap_2")
- ("data-tab" "account")
- (div
- ("class" "card_nest w_full")
- (div
- ("class" "card small flex items_center gap_2")
- (div
- ("class" "notification")
- ("style" "width: 46px")
- (icon (text "stripe")))
-
- (b (str (text "marketplace:label.status"))))
-
- (div
- ("class" "card")
- (text "{% if user.seller_data.account_id -%}")
- (text "{% if user.seller_data.completed_onboarding -%}")
- ; completed onboarding + has stripe account linked
- (button
- ("onclick" "trigger('seller::login')")
- (icon (text "arrow-right"))
- (str (text "marketplace:action.open_seller_dashboard")))
- (text "{% else %}")
- ; not completed onboarding
- (p (text "You've not finished setting up your Stripe account."))
- (p (text "Please complete onboarding to accept payments."))
-
- (button
- ("onclick" "trigger('seller::onboarding')")
- (icon (text "arrow-right"))
- (str (text "marketplace:action.finsh_setting_up_account")))
- (text "{%- endif %}")
- (text "{% else %}")
- ; doesn't have a stripe account linked
- (button
- ("onclick" "trigger('seller::register')")
- (icon (text "arrow-right"))
- (str (text "marketplace:action.get_started")))
- (text "{%- endif %}"))))
-
- (div
- ("class" "card w_full lowered hidden flex flex_col gap_2")
- ("data-tab" "products")
- (div
- ("class" "card w_full flex flex_wrap gap_2")
- )))
-
-(text "{% endblock %}")
diff --git a/crates/app/src/public/js/me.js b/crates/app/src/public/js/me.js
index 9b8ad1d..e1f7def 100644
--- a/crates/app/src/public/js/me.js
+++ b/crates/app/src/public/js/me.js
@@ -1205,60 +1205,3 @@
]);
});
})();
-
-(() => {
- const self = reg_ns("seller");
-
- self.define("register", async () => {
- await trigger("atto::debounce", ["seller::register"]);
-
- if (
- !(await trigger("atto::confirm", [
- "Are you sure you want to do this?",
- ]))
- ) {
- return;
- }
-
- const res = await (
- await fetch("/api/v1/service_hooks/stripe/seller/register", {
- method: "POST",
- })
- ).json();
-
- trigger("atto::toast", [res.ok ? "success" : "error", res.message]);
- self.onboarding();
- });
-
- self.define("onboarding", async () => {
- await trigger("atto::debounce", ["seller::onboarding"]);
-
- const res = await (
- await fetch("/api/v1/service_hooks/stripe/seller/onboarding", {
- method: "POST",
- })
- ).json();
-
- trigger("atto::toast", [res.ok ? "success" : "error", res.message]);
-
- if (res.ok) {
- window.location.href = res.payload;
- }
- });
-
- self.define("login", async () => {
- await trigger("atto::debounce", ["seller::login"]);
-
- const res = await (
- await fetch("/api/v1/service_hooks/stripe/seller/login", {
- method: "POST",
- })
- ).json();
-
- trigger("atto::toast", [res.ok ? "success" : "error", res.message]);
-
- if (res.ok) {
- window.location.href = res.payload;
- }
- });
-})();
diff --git a/crates/app/src/routes/api/v1/auth/connections/stripe.rs b/crates/app/src/routes/api/v1/auth/connections/stripe.rs
index 837d2d3..992deac 100644
--- a/crates/app/src/routes/api/v1/auth/connections/stripe.rs
+++ b/crates/app/src/routes/api/v1/auth/connections/stripe.rs
@@ -1,7 +1,5 @@
-use std::{str::FromStr, time::Duration};
-
+use std::time::Duration;
use axum::{http::HeaderMap, response::IntoResponse, Extension, Json};
-use crate::cookie::CookieJar;
use tetratto_core::model::{
auth::{Notification, User},
moderation::AuditLogEntry,
@@ -9,7 +7,7 @@ use tetratto_core::model::{
ApiReturn, Error,
};
use stripe::{EventObject, EventType};
-use crate::{get_user_from_token, State};
+use crate::State;
pub async fn stripe_webhook(
Extension(data): Extension,
@@ -471,145 +469,3 @@ pub async fn stripe_webhook(
payload: (),
})
}
-
-pub async fn onboarding_account_link_request(
- jar: CookieJar,
- Extension(data): Extension,
-) -> impl IntoResponse {
- let data = &(data.read().await);
- let user = match get_user_from_token!(jar, data.0) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- if user.seller_data.account_id.is_some() {
- return Json(Error::NotAllowed.into());
- }
-
- let client = match data.3 {
- Some(ref c) => c,
- None => return Json(Error::Unknown.into()),
- };
-
- match stripe::AccountLink::create(
- &client,
- stripe::CreateAccountLink {
- account: match user.seller_data.account_id {
- Some(id) => stripe::AccountId::from_str(&id).unwrap(),
- None => return Json(Error::NotAllowed.into()),
- },
- type_: stripe::AccountLinkType::AccountOnboarding,
- collect: None,
- expand: &[],
- refresh_url: Some(&format!(
- "{}/auth/connections_link/seller/refresh",
- data.0.0.0.host
- )),
- return_url: Some(&format!(
- "{}/auth/connections_link/seller/return",
- data.0.0.0.host
- )),
- collection_options: None,
- },
- )
- .await
- {
- Ok(x) => Json(ApiReturn {
- ok: true,
- message: "Acceptable".to_string(),
- payload: Some(x.url),
- }),
- Err(e) => Json(Error::MiscError(e.to_string()).into()),
- }
-}
-
-pub async fn create_seller_account_request(
- jar: CookieJar,
- Extension(data): Extension,
-) -> impl IntoResponse {
- let data = &(data.read().await);
- let mut user = match get_user_from_token!(jar, data.0) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- if user.seller_data.account_id.is_some() {
- return Json(Error::NotAllowed.into());
- }
-
- let client = match data.3 {
- Some(ref c) => c,
- None => return Json(Error::Unknown.into()),
- };
-
- let account = match stripe::Account::create(
- &client,
- stripe::CreateAccount {
- type_: Some(stripe::AccountType::Express),
- capabilities: Some(stripe::CreateAccountCapabilities {
- card_payments: Some(stripe::CreateAccountCapabilitiesCardPayments {
- requested: Some(true),
- }),
- transfers: Some(stripe::CreateAccountCapabilitiesTransfers {
- requested: Some(true),
- }),
- ..Default::default()
- }),
- ..Default::default()
- },
- )
- .await
- {
- Ok(a) => a,
- Err(e) => return Json(Error::MiscError(e.to_string()).into()),
- };
-
- user.seller_data.account_id = Some(account.id.to_string());
- match data
- .0
- .update_user_seller_data(user.id, user.seller_data)
- .await
- {
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "Acceptable".to_string(),
- payload: (),
- }),
- Err(e) => return Json(e.into()),
- }
-}
-
-pub async fn login_link_request(
- jar: CookieJar,
- Extension(data): Extension,
-) -> impl IntoResponse {
- let data = &(data.read().await);
- let user = match get_user_from_token!(jar, data.0) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- if user.seller_data.account_id.is_none() | !user.seller_data.completed_onboarding {
- return Json(Error::NotAllowed.into());
- }
-
- let client = match data.3 {
- Some(ref c) => c,
- None => return Json(Error::Unknown.into()),
- };
-
- match stripe::LoginLink::create(
- &client,
- &stripe::AccountId::from_str(&user.seller_data.account_id.unwrap()).unwrap(),
- &data.0.0.0.host,
- )
- .await
- {
- Ok(x) => Json(ApiReturn {
- ok: true,
- message: "Acceptable".to_string(),
- payload: Some(x.url),
- }),
- Err(e) => Json(Error::MiscError(e.to_string()).into()),
- }
-}
diff --git a/crates/app/src/routes/api/v1/auth/profile.rs b/crates/app/src/routes/api/v1/auth/profile.rs
index a4ec514..969cbce 100644
--- a/crates/app/src/routes/api/v1/auth/profile.rs
+++ b/crates/app/src/routes/api/v1/auth/profile.rs
@@ -1,4 +1,4 @@
-use std::{str::FromStr, time::Duration};
+use std::time::Duration;
use crate::{
get_user_from_token,
model::{ApiReturn, Error},
@@ -572,28 +572,11 @@ pub async fn delete_user_request(
.delete_user(id, &req.password, user.permissions.check_manager())
.await
{
- Ok(ua) => {
- // delete stripe user
- if let Some(stripe_id) = ua.seller_data.account_id
- && let Some(ref client) = data.3
- {
- if let Err(e) = stripe::Account::delete(
- &client,
- &stripe::AccountId::from_str(&stripe_id).unwrap(),
- )
- .await
- {
- return Json(Error::MiscError(e.to_string()).into());
- }
- }
-
- // ...
- Json(ApiReturn {
- ok: true,
- message: "User deleted".to_string(),
- payload: (),
- })
- }
+ Ok(_) => Json(ApiReturn {
+ ok: true,
+ message: "User deleted".to_string(),
+ payload: (),
+ }),
Err(e) => Json(e.into()),
}
}
diff --git a/crates/app/src/routes/api/v1/mod.rs b/crates/app/src/routes/api/v1/mod.rs
index 694ad29..5539bf4 100644
--- a/crates/app/src/routes/api/v1/mod.rs
+++ b/crates/app/src/routes/api/v1/mod.rs
@@ -8,7 +8,6 @@ pub mod journals;
pub mod letters;
pub mod notes;
pub mod notifications;
-pub mod products;
pub mod reactions;
pub mod reports;
pub mod requests;
@@ -34,7 +33,6 @@ use tetratto_core::model::{
littleweb::{DomainData, DomainTld, ServiceFsEntry},
oauth::AppScope,
permissions::{FinePermission, SecondaryPermission},
- products::{ProductPrice, ProductType},
reactions::AssetType,
stacks::{StackMode, StackPrivacy, StackSort},
};
@@ -566,18 +564,6 @@ pub fn routes() -> Router {
"/service_hooks/stripe",
post(auth::connections::stripe::stripe_webhook),
)
- .route(
- "/service_hooks/stripe/seller/register",
- post(auth::connections::stripe::create_seller_account_request),
- )
- .route(
- "/service_hooks/stripe/seller/onboarding",
- post(auth::connections::stripe::onboarding_account_link_request),
- )
- .route(
- "/service_hooks/stripe/seller/login",
- post(auth::connections::stripe::login_link_request),
- )
// channels
.route("/channels", post(channels::channels::create_request))
.route(
@@ -716,17 +702,6 @@ pub fn routes() -> Router {
.route("/domains/{id}", get(domains::get_request))
.route("/domains/{id}", delete(domains::delete_request))
.route("/domains/{id}/data", post(domains::update_data_request))
- // products
- .route("/products", get(products::list_request))
- .route("/products", post(products::create_request))
- .route("/products/{id}", get(products::get_request))
- .route("/products/{id}", delete(products::delete_request))
- .route("/products/{id}/name", post(products::update_name_request))
- .route(
- "/products/{id}/description",
- post(products::update_description_request),
- )
- .route("/products/{id}/price", post(products::update_price_request))
// letters
.route("/letters", post(letters::create_request))
.route("/letters/{id}", get(letters::get_request))
@@ -1207,29 +1182,6 @@ pub struct UpdateDomainData {
pub data: Vec<(String, DomainData)>,
}
-#[derive(Deserialize)]
-pub struct CreateProduct {
- pub name: String,
- pub description: String,
- pub product_type: ProductType,
- pub price: ProductPrice,
-}
-
-#[derive(Deserialize)]
-pub struct UpdateProductName {
- pub name: String,
-}
-
-#[derive(Deserialize)]
-pub struct UpdateProductDescription {
- pub description: String,
-}
-
-#[derive(Deserialize)]
-pub struct UpdateProductPrice {
- pub price: ProductPrice,
-}
-
#[derive(Deserialize)]
pub struct UpdateUploadAlt {
pub alt: String,
diff --git a/crates/app/src/routes/api/v1/products.rs b/crates/app/src/routes/api/v1/products.rs
deleted file mode 100644
index 4d53814..0000000
--- a/crates/app/src/routes/api/v1/products.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-use crate::{
- get_user_from_token,
- image::{save_webp_buffer, JsonMultipart},
- routes::{
- api::v1::{
- communities::posts::MAXIMUM_FILE_SIZE, CreateProduct, UpdateProductDescription,
- UpdateProductName, UpdateProductPrice,
- },
- pages::PaginatedQuery,
- },
- State,
-};
-use axum::{
- extract::{Path, Query},
- response::IntoResponse,
- Extension, Json,
-};
-use crate::cookie::CookieJar;
-use tetratto_core::model::{
- oauth,
- products::Product,
- uploads::{MediaType, MediaUpload},
- ApiReturn, Error,
-};
-
-pub async fn get_request(
- Path(id): Path,
- Extension(data): Extension,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- match data.get_product_by_id(id).await {
- Ok(x) => Json(ApiReturn {
- ok: true,
- message: "Success".to_string(),
- payload: Some(x),
- }),
- Err(e) => return Json(e.into()),
- }
-}
-
-pub async fn list_request(
- jar: CookieJar,
- Extension(data): Extension,
- Query(props): Query,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserReadProducts) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- match data.get_products_by_user(user.id, 12, props.page).await {
- Ok(x) => Json(ApiReturn {
- ok: true,
- message: "Success".to_string(),
- payload: Some(x),
- }),
- Err(e) => Json(e.into()),
- }
-}
-
-pub async fn create_request(
- jar: CookieJar,
- Extension(data): Extension,
- JsonMultipart(uploads, req): JsonMultipart,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateProducts) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- if uploads.len() > 4 {
- return Json(
- Error::MiscError("Too many uploads. Please use a maximum of 4".to_string()).into(),
- );
- }
-
- let mut product = Product::new(
- user.id,
- req.name,
- req.description,
- req.price,
- req.product_type,
- );
-
- // check sizes
- for img in &uploads {
- if img.len() > MAXIMUM_FILE_SIZE {
- return Json(Error::FileTooLarge.into());
- }
- }
-
- // create uploads
- for _ in 0..uploads.len() {
- product.uploads.push(
- match data
- .create_upload(MediaUpload::new(MediaType::Webp, product.owner))
- .await
- {
- Ok(u) => u.id,
- Err(e) => return Json(e.into()),
- },
- );
- }
-
- let product_uploads = product.uploads.clone();
- match data.create_product(product).await {
- Ok(x) => {
- // store uploads
- for (i, upload_id) in product_uploads.iter().enumerate() {
- let image = match uploads.get(i) {
- Some(img) => img,
- None => {
- if let Err(e) = data.delete_upload(*upload_id).await {
- return Json(e.into());
- }
-
- continue;
- }
- };
-
- let upload = match data.get_upload_by_id(*upload_id).await {
- Ok(u) => u,
- Err(e) => return Json(e.into()),
- };
-
- if let Err(e) =
- save_webp_buffer(&upload.path(&data.0.0).to_string(), image.to_vec(), None)
- {
- return Json(Error::MiscError(e.to_string()).into());
- }
- }
-
- // ...
- Json(ApiReturn {
- ok: true,
- message: "Product created".to_string(),
- payload: x.id.to_string(),
- })
- }
- Err(e) => Json(e.into()),
- }
-}
-
-pub async fn update_name_request(
- jar: CookieJar,
- Extension(data): Extension,
- Path(id): Path,
- Json(req): Json,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProducts) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- match data.update_product_name(id, &user, &req.name).await {
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "Product updated".to_string(),
- payload: (),
- }),
- Err(e) => Json(e.into()),
- }
-}
-
-pub async fn update_description_request(
- jar: CookieJar,
- Extension(data): Extension,
- Path(id): Path,
- Json(req): Json,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProducts) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- match data
- .update_product_description(id, &user, &req.description)
- .await
- {
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "Product updated".to_string(),
- payload: (),
- }),
- Err(e) => Json(e.into()),
- }
-}
-
-pub async fn update_price_request(
- jar: CookieJar,
- Extension(data): Extension,
- Path(id): Path,
- Json(req): Json,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProducts) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- match data.update_product_price(id, &user, req.price).await {
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "Product updated".to_string(),
- payload: (),
- }),
- Err(e) => Json(e.into()),
- }
-}
-
-pub async fn delete_request(
- jar: CookieJar,
- Extension(data): Extension,
- Path(id): Path,
-) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageProducts) {
- Some(ua) => ua,
- None => return Json(Error::NotAllowed.into()),
- };
-
- match data.delete_product(id, &user).await {
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "Product deleted".to_string(),
- payload: (),
- }),
- Err(e) => Json(e.into()),
- }
-}
diff --git a/crates/app/src/routes/pages/marketplace.rs b/crates/app/src/routes/pages/marketplace.rs
deleted file mode 100644
index 8d5a3be..0000000
--- a/crates/app/src/routes/pages/marketplace.rs
+++ /dev/null
@@ -1,107 +0,0 @@
-use super::render_error;
-use crate::{
- assets::initial_context, get_lang, get_user_from_token, State, routes::pages::PaginatedQuery,
-};
-use axum::{
- extract::Query,
- response::{Html, IntoResponse},
- Extension,
-};
-use crate::cookie::CookieJar;
-use tetratto_core::model::Error;
-
-/// `/settings/seller`
-pub async fn seller_settings_request(
- jar: CookieJar,
- Extension(data): Extension,
- Query(props): Query,
-) -> impl IntoResponse {
- let data = data.read().await;
- let user = match get_user_from_token!(jar, data.0) {
- Some(ua) => ua,
- None => {
- return Err(Html(
- render_error(Error::NotAllowed, &jar, &data, &None).await,
- ));
- }
- };
-
- let products = match data.0.get_products_by_user(user.id, 12, props.page).await {
- Ok(x) => x,
- Err(e) => return Err(Html(render_error(e, &jar, &data, &None).await)),
- };
-
- let lang = get_lang!(jar, data.0);
- let mut context = initial_context(&data.0.0.0, lang, &Some(user)).await;
-
- context.insert("list", &products);
- context.insert("page", &props.page);
-
- // return
- Ok(Html(
- data.1.render("marketplace/seller.html", &context).unwrap(),
- ))
-}
-
-pub async fn connection_return_request(
- jar: CookieJar,
- Extension(data): Extension,
-) -> impl IntoResponse {
- let data = data.read().await;
- let mut user = match get_user_from_token!(jar, data.0) {
- Some(ua) => ua,
- None => {
- return Err(Html(
- render_error(Error::NotAllowed, &jar, &data, &None).await,
- ));
- }
- };
-
- // update user
- user.seller_data.completed_onboarding = true;
- if let Err(e) = data
- .0
- .update_user_seller_data(user.id, user.seller_data.clone())
- .await
- {
- return Err(Html(render_error(e, &jar, &data, &None).await));
- }
-
- // ...
- let lang = get_lang!(jar, data.0);
- let mut context = initial_context(&data.0.0.0, lang, &Some(user)).await;
- context.insert("connection_type", "return");
-
- // return
- Ok(Html(
- data.1
- .render("auth/seller_connection.html", &context)
- .unwrap(),
- ))
-}
-
-pub async fn connection_reload_request(
- jar: CookieJar,
- Extension(data): Extension,
-) -> impl IntoResponse {
- let data = data.read().await;
- let user = match get_user_from_token!(jar, data.0) {
- Some(ua) => ua,
- None => {
- return Err(Html(
- render_error(Error::NotAllowed, &jar, &data, &None).await,
- ));
- }
- };
-
- let lang = get_lang!(jar, data.0);
- let mut context = initial_context(&data.0.0.0, lang, &Some(user)).await;
- context.insert("connection_type", "reload");
-
- // return
- Ok(Html(
- data.1
- .render("auth/seller_connection.html", &context)
- .unwrap(),
- ))
-}
diff --git a/crates/app/src/routes/pages/mod.rs b/crates/app/src/routes/pages/mod.rs
index 01f24d6..93cfc9a 100644
--- a/crates/app/src/routes/pages/mod.rs
+++ b/crates/app/src/routes/pages/mod.rs
@@ -6,7 +6,6 @@ pub mod forge;
pub mod journals;
pub mod littleweb;
pub mod mail;
-pub mod marketplace;
pub mod misc;
pub mod mod_panel;
pub mod profile;
@@ -77,14 +76,6 @@ pub fn routes() -> Router {
"/auth/connections_link/app/{id}",
get(developer::connection_callback_request),
)
- .route(
- "/auth/connections_link/seller/reload",
- get(marketplace::connection_reload_request),
- )
- .route(
- "/auth/connections_link/seller/return",
- get(marketplace::connection_return_request),
- )
// profile
.route("/settings", get(profile::settings_request))
.route("/@{username}", get(profile::posts_request))
@@ -163,11 +154,6 @@ pub fn routes() -> Router {
.route("/domains/{id}", get(littleweb::domain_request))
.route("/net", get(littleweb::browser_home_request))
.route("/net/{*uri}", get(littleweb::browser_request))
- // marketplace
- .route(
- "/settings/seller",
- get(marketplace::seller_settings_request),
- )
// mail
.route("/mail", get(mail::received_request))
.route("/mail/sent", get(mail::sent_request))
diff --git a/crates/core/src/database/auth.rs b/crates/core/src/database/auth.rs
index e0aef74..05698ff 100644
--- a/crates/core/src/database/auth.rs
+++ b/crates/core/src/database/auth.rs
@@ -1,8 +1,7 @@
use super::common::NAME_REGEX;
use oiseau::cache::Cache;
use crate::model::auth::{
- Achievement, AchievementName, AchievementRarity, Notification, StripeSellerData,
- UserConnections, ACHIEVEMENTS,
+ Achievement, AchievementName, AchievementRarity, Notification, UserConnections, ACHIEVEMENTS,
};
use crate::model::moderation::AuditLogEntry;
use crate::model::oauth::AuthGrant;
@@ -125,11 +124,10 @@ impl DataManager {
awaiting_purchase: get!(x->24(i32)) as i8 == 1,
was_purchased: get!(x->25(i32)) as i8 == 1,
browser_session: get!(x->26(String)),
- seller_data: serde_json::from_str(&get!(x->27(String)).to_string()).unwrap(),
- ban_reason: get!(x->28(String)),
- channel_mutes: serde_json::from_str(&get!(x->29(String)).to_string()).unwrap(),
- is_deactivated: get!(x->30(i32)) as i8 == 1,
- ban_expire: get!(x->31(i64)) as usize,
+ ban_reason: get!(x->27(String)),
+ channel_mutes: serde_json::from_str(&get!(x->28(String)).to_string()).unwrap(),
+ is_deactivated: get!(x->29(i32)) as i8 == 1,
+ ban_expire: get!(x->30(i64)) as usize,
}
}
@@ -286,7 +284,7 @@ impl DataManager {
let res = execute!(
&conn,
- "INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32)",
+ "INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31)",
params![
&(data.id as i64),
&(data.created as i64),
@@ -315,7 +313,6 @@ impl DataManager {
&if data.awaiting_purchase { 1_i32 } else { 0_i32 },
&if data.was_purchased { 1_i32 } else { 0_i32 },
&data.browser_session,
- &serde_json::to_string(&data.seller_data).unwrap(),
&data.ban_reason,
&serde_json::to_string(&data.channel_mutes).unwrap(),
&if data.is_deactivated { 1_i32 } else { 0_i32 },
@@ -1058,7 +1055,6 @@ impl DataManager {
auto_method!(update_user_achievements(Vec)@get_user_by_id -> "UPDATE users SET achievements = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
auto_method!(update_user_invite_code(i64)@get_user_by_id -> "UPDATE users SET invite_code = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
auto_method!(update_user_browser_session(&str)@get_user_by_id -> "UPDATE users SET browser_session = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
- auto_method!(update_user_seller_data(StripeSellerData)@get_user_by_id -> "UPDATE users SET seller_data = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
auto_method!(update_user_ban_reason(&str)@get_user_by_id -> "UPDATE users SET ban_reason = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
auto_method!(update_user_channel_mutes(Vec)@get_user_by_id -> "UPDATE users SET channel_mutes = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
auto_method!(update_user_ban_expire(i64)@get_user_by_id -> "UPDATE users SET ban_expire = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
diff --git a/crates/core/src/database/common.rs b/crates/core/src/database/common.rs
index 5e10783..71e792d 100644
--- a/crates/core/src/database/common.rs
+++ b/crates/core/src/database/common.rs
@@ -42,7 +42,6 @@ impl DataManager {
execute!(&conn, common::CREATE_TABLE_INVITE_CODES).unwrap();
execute!(&conn, common::CREATE_TABLE_DOMAINS).unwrap();
execute!(&conn, common::CREATE_TABLE_SERVICES).unwrap();
- execute!(&conn, common::CREATE_TABLE_PRODUCTS).unwrap();
execute!(&conn, common::CREATE_TABLE_APP_DATA).unwrap();
execute!(&conn, common::CREATE_TABLE_LETTERS).unwrap();
diff --git a/crates/core/src/database/drivers/common.rs b/crates/core/src/database/drivers/common.rs
index bccbfb9..4881179 100644
--- a/crates/core/src/database/drivers/common.rs
+++ b/crates/core/src/database/drivers/common.rs
@@ -30,6 +30,5 @@ pub const CREATE_TABLE_MESSAGE_REACTIONS: &str = include_str!("./sql/create_mess
pub const CREATE_TABLE_INVITE_CODES: &str = include_str!("./sql/create_invite_codes.sql");
pub const CREATE_TABLE_DOMAINS: &str = include_str!("./sql/create_domains.sql");
pub const CREATE_TABLE_SERVICES: &str = include_str!("./sql/create_services.sql");
-pub const CREATE_TABLE_PRODUCTS: &str = include_str!("./sql/create_products.sql");
pub const CREATE_TABLE_APP_DATA: &str = include_str!("./sql/create_app_data.sql");
pub const CREATE_TABLE_LETTERS: &str = include_str!("./sql/create_letters.sql");
diff --git a/crates/core/src/database/drivers/sql/create_products.sql b/crates/core/src/database/drivers/sql/create_products.sql
deleted file mode 100644
index 4a972aa..0000000
--- a/crates/core/src/database/drivers/sql/create_products.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-CREATE TABLE IF NOT EXISTS products (
- id BIGINT NOT NULL PRIMARY KEY,
- created BIGINT NOT NULL,
- owner BIGINT NOT NULL,
- name TEXT NOT NULL,
- description TEXT NOT NULL,
- likes INT NOT NULL,
- dislikes INT NOT NULL,
- product_type TEXT NOT NULL,
- price TEXT NOT NULL,
- uploads TEXT NOT NULL
-)
diff --git a/crates/core/src/database/drivers/sql/create_users.sql b/crates/core/src/database/drivers/sql/create_users.sql
index 06e86ef..e68a27c 100644
--- a/crates/core/src/database/drivers/sql/create_users.sql
+++ b/crates/core/src/database/drivers/sql/create_users.sql
@@ -26,7 +26,6 @@ CREATE TABLE IF NOT EXISTS users (
awaiting_purchase INT NOT NULL,
was_purchased INT NOT NULL,
browser_session TEXT NOT NULL,
- seller_data TEXT NOT NULL,
ban_reason TEXT NOT NULL,
channel_mutes TEXT NOT NULL,
is_deactivated INT NOT NULL,
diff --git a/crates/core/src/database/drivers/sql/version_migrations.sql b/crates/core/src/database/drivers/sql/version_migrations.sql
index 5c6c51e..5f3148c 100644
--- a/crates/core/src/database/drivers/sql/version_migrations.sql
+++ b/crates/core/src/database/drivers/sql/version_migrations.sql
@@ -29,3 +29,7 @@ ADD COLUMN IF NOT EXISTS topic BIGINT DEFAULT 0;
-- users ban_expire
ALTER TABLE users
ADD COLUMN IF NOT EXISTS ban_expire BIGINT DEFAULT 0;
+
+-- remove users seller_data
+ALTER TABLE users
+DROP COLUMN IF EXISTS seller_data;
diff --git a/crates/core/src/database/mod.rs b/crates/core/src/database/mod.rs
index 218bcd6..5650d7d 100644
--- a/crates/core/src/database/mod.rs
+++ b/crates/core/src/database/mod.rs
@@ -23,7 +23,6 @@ mod notifications;
mod polls;
mod pollvotes;
mod posts;
-mod products;
mod questions;
mod reactions;
mod reports;
diff --git a/crates/core/src/database/products.rs b/crates/core/src/database/products.rs
deleted file mode 100644
index b5e32a5..0000000
--- a/crates/core/src/database/products.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use crate::model::{
- auth::User,
- permissions::{FinePermission, SecondaryPermission},
- products::{Product, ProductPrice},
- Error, Result,
-};
-use crate::{auto_method, DataManager};
-use oiseau::{cache::Cache, execute, get, params, query_rows, PostgresRow};
-
-impl DataManager {
- /// Get a [`Product`] from an SQL row.
- pub(crate) fn get_product_from_row(x: &PostgresRow) -> Product {
- Product {
- id: get!(x->0(i64)) as usize,
- created: get!(x->1(i64)) as usize,
- owner: get!(x->2(i64)) as usize,
- name: get!(x->3(String)),
- description: get!(x->4(String)),
- likes: get!(x->5(i32)) as isize,
- dislikes: get!(x->6(i32)) as isize,
- product_type: serde_json::from_str(&get!(x->7(String))).unwrap(),
- price: serde_json::from_str(&get!(x->8(String))).unwrap(),
- uploads: serde_json::from_str(&get!(x->9(String))).unwrap(),
- }
- }
-
- auto_method!(get_product_by_id(usize as i64)@get_product_from_row -> "SELECT * FROM products WHERE id = $1" --name="product" --returns=Product --cache-key-tmpl="atto.product:{}");
-
- /// Get all products by user.
- ///
- /// # Arguments
- /// * `id` - the ID of the user to fetch products for
- /// * `batch`
- /// * `page`
- pub async fn get_products_by_user(
- &self,
- id: usize,
- batch: usize,
- page: usize,
- ) -> Result> {
- let conn = match self.0.connect().await {
- Ok(c) => c,
- Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
- };
-
- let res = query_rows!(
- &conn,
- "SELECT * FROM products WHERE owner = $1 ORDER BY created DESC LIMIT {} OFFSET {}",
- &[&(id as i64), &(batch as i64), &((page * batch) as i64)],
- |x| { Self::get_product_from_row(x) }
- );
-
- if res.is_err() {
- return Err(Error::GeneralNotFound("product".to_string()));
- }
-
- Ok(res.unwrap())
- }
-
- /// Get all products by user.
- ///
- /// # Arguments
- /// * `id` - the ID of the user to fetch products for
- pub async fn get_products_by_user_all(&self, id: usize) -> Result> {
- let conn = match self.0.connect().await {
- Ok(c) => c,
- Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
- };
-
- let res = query_rows!(
- &conn,
- "SELECT * FROM products WHERE owner = $1 ORDER BY created DESC",
- &[&(id as i64)],
- |x| { Self::get_product_from_row(x) }
- );
-
- if res.is_err() {
- return Err(Error::GeneralNotFound("product".to_string()));
- }
-
- Ok(res.unwrap())
- }
-
- const MAXIMUM_FREE_PRODUCTS: usize = 15;
-
- /// Create a new product in the database.
- ///
- /// # Arguments
- /// * `data` - a mock [`Product`] object to insert
- pub async fn create_product(&self, data: Product) -> Result {
- // check values
- if data.name.trim().len() < 2 {
- return Err(Error::DataTooShort("name".to_string()));
- } else if data.name.len() > 128 {
- return Err(Error::DataTooLong("name".to_string()));
- }
-
- // check number of products
- let owner = self.get_user_by_id(data.owner).await?;
-
- if !owner.permissions.check(FinePermission::SUPPORTER) {
- let products = self
- .get_table_row_count_where("products", &format!("owner = {}", owner.id))
- .await? as usize;
-
- if products >= Self::MAXIMUM_FREE_PRODUCTS {
- return Err(Error::MiscError(
- "You already have the maximum number of products you can have".to_string(),
- ));
- }
- }
-
- // ...
- let conn = match self.0.connect().await {
- Ok(c) => c,
- Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
- };
-
- let res = execute!(
- &conn,
- "INSERT INTO products VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
- params![
- &(data.id as i64),
- &(data.created as i64),
- &(data.owner as i64),
- &data.name,
- &data.description,
- &0_i32,
- &0_i32,
- &serde_json::to_string(&data.product_type).unwrap(),
- &serde_json::to_string(&data.price).unwrap(),
- &serde_json::to_string(&data.uploads).unwrap(),
- ]
- );
-
- if let Err(e) = res {
- return Err(Error::DatabaseError(e.to_string()));
- }
-
- Ok(data)
- }
-
- pub async fn delete_product(&self, id: usize, user: &User) -> Result<()> {
- let product = self.get_product_by_id(id).await?;
-
- // check user permission
- if user.id != product.owner
- && !user
- .secondary_permissions
- .check(SecondaryPermission::MANAGE_PRODUCTS)
- {
- return Err(Error::NotAllowed);
- }
-
- // ...
- let conn = match self.0.connect().await {
- Ok(c) => c,
- Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
- };
-
- let res = execute!(&conn, "DELETE FROM products WHERE id = $1", &[&(id as i64)]);
-
- if let Err(e) = res {
- return Err(Error::DatabaseError(e.to_string()));
- }
-
- // ...
- self.0.1.remove(format!("atto.product:{}", id)).await;
- Ok(())
- }
-
- auto_method!(update_product_name(&str)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET name = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
- auto_method!(update_product_description(&str)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET description = $1 WHERE id = $2" --cache-key-tmpl="atto.product:{}");
- auto_method!(update_product_price(ProductPrice)@get_product_by_id:FinePermission::MANAGE_USERS; -> "UPDATE products SET price = $1 WHERE id = $2" --serde --cache-key-tmpl="atto.product:{}");
-}
diff --git a/crates/core/src/model/auth.rs b/crates/core/src/model/auth.rs
index 7622f1a..ef19586 100644
--- a/crates/core/src/model/auth.rs
+++ b/crates/core/src/model/auth.rs
@@ -79,9 +79,6 @@ pub struct User {
/// view pages which require authentication (all `$` routes).
#[serde(default)]
pub browser_session: String,
- /// Stripe connected account information (for Tetratto marketplace).
- #[serde(default)]
- pub seller_data: StripeSellerData,
/// The reason the user was banned.
#[serde(default)]
pub ban_reason: String,
@@ -355,14 +352,6 @@ pub struct UserSettings {
pub forum_signature: String,
}
-#[derive(Clone, Debug, Serialize, Deserialize, Default)]
-pub struct StripeSellerData {
- #[serde(default)]
- pub account_id: Option,
- #[serde(default)]
- pub completed_onboarding: bool,
-}
-
fn mime_avif() -> String {
"image/avif".to_string()
}
@@ -407,7 +396,6 @@ impl User {
awaiting_purchase: false,
was_purchased: false,
browser_session: String::new(),
- seller_data: StripeSellerData::default(),
ban_reason: String::new(),
channel_mutes: Vec::new(),
is_deactivated: false,
diff --git a/crates/core/src/model/mod.rs b/crates/core/src/model/mod.rs
index 5bf8c52..b78dd37 100644
--- a/crates/core/src/model/mod.rs
+++ b/crates/core/src/model/mod.rs
@@ -11,7 +11,6 @@ pub mod mail;
pub mod moderation;
pub mod oauth;
pub mod permissions;
-pub mod products;
pub mod reactions;
pub mod requests;
pub mod socket;
diff --git a/crates/core/src/model/products.rs b/crates/core/src/model/products.rs
deleted file mode 100644
index 2b90ca5..0000000
--- a/crates/core/src/model/products.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use std::fmt::Display;
-
-use serde::{Serialize, Deserialize};
-use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct Product {
- pub id: usize,
- pub created: usize,
- pub owner: usize,
- pub name: String,
- pub description: String,
- pub likes: isize,
- pub dislikes: isize,
- pub product_type: ProductType,
- pub price: ProductPrice,
- /// Optional uploads to accompany the product title and description. Maximum of 4.
- pub uploads: Vec,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub enum ProductType {
- /// Text + images.
- Data,
- /// When a commission product is purchased, the creator will receive a request
- /// prompting them to respond with text + images.
- ///
- /// This is the only product type which does not immediately return data to the
- /// customer, as seller input is required.
- ///
- /// If the request is deleted, the purchase should be immediately refunded.
- ///
- /// Commissions are paid beforehand to prevent theft. This means it is vital
- /// that refunds are enforced.
- Commission,
-}
-
-/// A currency.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub enum Currency {
- USD,
- EUR,
- GBP,
-}
-
-impl Display for Currency {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str(match self {
- Currency::USD => "$",
- Currency::EUR => "€",
- Currency::GBP => "£",
- })
- }
-}
-
-/// Price in USD. `(dollars, cents)`.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct ProductPrice(u64, u64, Currency);
-
-impl Display for ProductPrice {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str(&format!("{}{}.{}", self.2, self.0, self.1))
- }
-}
-
-impl Product {
- /// Create a new [`Product`].
- pub fn new(
- owner: usize,
- name: String,
- description: String,
- price: ProductPrice,
- r#type: ProductType,
- ) -> Self {
- Self {
- id: Snowflake::new().to_string().parse::().unwrap(),
- created: unix_epoch_timestamp(),
- owner,
- name,
- description,
- likes: 0,
- dislikes: 0,
- product_type: r#type,
- price,
- uploads: Vec::new(),
- }
- }
-}