diff --git a/crates/app/src/public/html/profile/settings.lisp b/crates/app/src/public/html/profile/settings.lisp
index c5566c7..e3d775b 100644
--- a/crates/app/src/public/html/profile/settings.lisp
+++ b/crates/app/src/public/html/profile/settings.lisp
@@ -1537,7 +1537,7 @@
],
[
[\"auto_full_unlist\", \"Only publish my posts to my profile\"],
- \"{{ profile.settings.auto_unlist }}\",
+ \"{{ profile.settings.auto_full_unlist }}\",
\"checkbox\",
],
[
diff --git a/crates/app/src/routes/api/v1/auth/profile.rs b/crates/app/src/routes/api/v1/auth/profile.rs
index 8104c71..bb874fe 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::time::Duration;
+use std::{str::FromStr, time::Duration};
use crate::{
get_user_from_token,
model::{ApiReturn, Error},
@@ -451,8 +451,8 @@ pub async fn delete_user_request(
Extension(data): Extension,
Json(req): Json,
) -> impl IntoResponse {
- let data = &(data.read().await).0;
- let user = match get_user_from_token!(jar, data) {
+ let data = &(data.read().await);
+ let user = match get_user_from_token!(jar, data.0) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
@@ -461,6 +461,7 @@ pub async fn delete_user_request(
return Json(Error::NotAllowed.into());
} else if user.permissions.check(FinePermission::MANAGE_USERS) {
if let Err(e) = data
+ .0
.create_audit_log_entry(AuditLogEntry::new(
user.id,
format!("invoked `delete_user` with x value `{id}`"),
@@ -472,14 +473,32 @@ pub async fn delete_user_request(
}
match data
+ .0
.delete_user(id, &req.password, user.permissions.check_manager())
.await
{
- Ok(_) => Json(ApiReturn {
- ok: true,
- message: "User deleted".to_string(),
- payload: (),
- }),
+ 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: (),
+ })
+ }
Err(e) => Json(e.into()),
}
}
diff --git a/crates/core/src/database/auth.rs b/crates/core/src/database/auth.rs
index 9bfdd36..b8651ca 100644
--- a/crates/core/src/database/auth.rs
+++ b/crates/core/src/database/auth.rs
@@ -321,7 +321,7 @@ impl DataManager {
/// * `id` - the ID of the user
/// * `password` - the current password of the user
/// * `force` - if we should delete even if the given password is incorrect
- pub async fn delete_user(&self, id: usize, password: &str, force: bool) -> Result<()> {
+ pub async fn delete_user(&self, id: usize, password: &str, force: bool) -> Result {
let user = self.get_user_by_id(id).await?;
if (hash_salted(password.to_string(), user.salt.clone()) != user.password) && !force {
@@ -581,7 +581,7 @@ impl DataManager {
}
// ...
- Ok(())
+ Ok(user)
}
pub async fn update_user_verified_status(&self, id: usize, x: bool, user: User) -> Result<()> {
diff --git a/crates/core/src/model/mod.rs b/crates/core/src/model/mod.rs
index 2cd4955..b86ebfa 100644
--- a/crates/core/src/model/mod.rs
+++ b/crates/core/src/model/mod.rs
@@ -10,6 +10,7 @@ pub mod littleweb;
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
new file mode 100644
index 0000000..e7b5b41
--- /dev/null
+++ b/crates/core/src/model/products.rs
@@ -0,0 +1,58 @@
+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 title: String,
+ pub description: String,
+ pub likes: usize,
+ pub dislikes: usize,
+ pub r#type: ProductType,
+ pub stripe_id: String,
+ pub price: ProductPrice,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum ProductType {
+ /// Text + images.
+ Message,
+ /// 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.
+ Commission,
+}
+
+/// Price in USD. `(dollars, cents)`.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct ProductPrice(u64, u64);
+
+impl Product {
+ /// Create a new [`Product`].
+ pub fn new(
+ owner: usize,
+ title: String,
+ description: String,
+ price: ProductPrice,
+ r#type: ProductType,
+ ) -> Self {
+ Self {
+ id: Snowflake::new().to_string().parse::().unwrap(),
+ created: unix_epoch_timestamp(),
+ owner,
+ title,
+ description,
+ likes: 0,
+ dislikes: 0,
+ r#type,
+ stripe_id: String::new(),
+ price,
+ }
+ }
+}