add: product types

This commit is contained in:
trisua 2025-07-13 00:05:28 -04:00
parent aea764948c
commit 2705608903
5 changed files with 89 additions and 11 deletions

View file

@ -1537,7 +1537,7 @@
],
[
[\"auto_full_unlist\", \"Only publish my posts to my profile\"],
\"{{ profile.settings.auto_unlist }}\",
\"{{ profile.settings.auto_full_unlist }}\",
\"checkbox\",
],
[

View file

@ -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<State>,
Json(req): Json<DeleteUser>,
) -> 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(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()),
}
}

View file

@ -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<User> {
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<()> {

View file

@ -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;

View file

@ -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::<usize>().unwrap(),
created: unix_epoch_timestamp(),
owner,
title,
description,
likes: 0,
dislikes: 0,
r#type,
stripe_id: String::new(),
price,
}
}
}