Skip to content

Commit

Permalink
Enforce max number of unapproved listings per user (#502)
Browse files Browse the repository at this point in the history
  • Loading branch information
yzernik authored Jul 29, 2022
1 parent 6aa7346 commit 97ecbf2
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 60 deletions.
36 changes: 18 additions & 18 deletions sqlx-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -1840,6 +1840,24 @@
},
"query": "UPDATE adminsettings SET market_name = ?"
},
"856abbf24892e4e130199bd2ea8431884e251f991342693ad9b969ade91291f4": {
"describe": {
"columns": [
{
"name": "num_unapproved_listings",
"ordinal": 0,
"type_info": "Int"
}
],
"nullable": [
false
],
"parameters": {
"Right": 1
}
},
"query": "\nselect\n COUNT(listings.id) as num_unapproved_listings\nfrom\n listings\nWHERE\n listings.user_id = ?\nAND\n NOT listings.approved\n;"
},
"88bd66872045161c59db2a2eb7d828bb3fd56a96dbe99d9773a89b0d7b935538": {
"describe": {
"columns": [
Expand Down Expand Up @@ -2852,24 +2870,6 @@
},
"query": "select * from orders WHERE public_id = ?;"
},
"a3fb13620332abfa1a0d8f2bf50f09de3ab661fbbe0d96dcee225087a94fefe8": {
"describe": {
"columns": [
{
"name": "listing_count",
"ordinal": 0,
"type_info": "Int"
}
],
"nullable": [
false
],
"parameters": {
"Right": 2
}
},
"query": "\nselect count(id) as listing_count from listings\nWHERE\n user_id = ?\nAND\n created_time_ms > ?\nORDER BY listings.created_time_ms ASC;"
},
"a3fe2d4cad9d9d9df3846a058ca50313dc3578cbc863711163aa95a2178d7e44": {
"describe": {
"columns": [
Expand Down
70 changes: 42 additions & 28 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,16 @@ impl Default for UserSettings {

impl Listing {
/// Returns the id of the inserted row.
pub async fn insert(listing: Listing, db: &mut Connection<Db>) -> Result<i32, sqlx::Error> {
pub async fn insert(
listing: Listing,
max_unapproved_listings: u32,
db: &mut Connection<Db>,
) -> Result<i32, String> {
let mut tx = db
.begin()
.await
.map_err(|_| "failed to begin transaction.")?;

let price_sat: i64 = listing.price_sat.try_into().unwrap();
let created_time_ms: i64 = listing.created_time_ms.try_into().unwrap();

Expand All @@ -316,8 +325,38 @@ impl Listing {
listing.deactivated_by_admin,
created_time_ms,
)
.execute(&mut **db)
.await?;
.execute(&mut *tx)
.await
.map_err(|_| "failed to insert new listing.")?;

let num_unapproved_listings = sqlx::query!(
"
select
COUNT(listings.id) as num_unapproved_listings
from
listings
WHERE
listings.user_id = ?
AND
NOT listings.approved
;",
listing.user_id,
)
.fetch_one(&mut *tx)
.map_ok(|r| r.num_unapproved_listings as u32)
.await
.map_err(|_| "failed to get count of unapproved listings.")?;

if num_unapproved_listings > max_unapproved_listings {
return Err(format!(
"more than {:?} unapproved listings not allowed.",
max_unapproved_listings
));
}

tx.commit()
.await
.map_err(|_| "failed to commit transaction.")?;

Ok(insert_result.last_insert_rowid() as _)
}
Expand Down Expand Up @@ -452,31 +491,6 @@ AND NOT (deactivated_by_seller OR deactivated_by_admin)
Ok(())
}

pub async fn count_for_user_since_time_ms(
db: &mut Connection<Db>,
user_id: i32,
start_time_ms: u64,
) -> Result<u32, sqlx::Error> {
let start_time_ms_i64: i64 = start_time_ms.try_into().unwrap();

let listing_count = sqlx::query!(
"
select count(id) as listing_count from listings
WHERE
user_id = ?
AND
created_time_ms > ?
ORDER BY listings.created_time_ms ASC;",
user_id,
start_time_ms_i64,
)
.fetch_one(&mut **db)
.map_ok(|r| r.listing_count)
.await?;

Ok(listing_count.try_into().unwrap())
}

pub async fn num_pending(db: &mut Connection<Db>) -> Result<u32, sqlx::Error> {
let num_listings = sqlx::query!(
"
Expand Down
17 changes: 3 additions & 14 deletions src/new_listing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rocket_auth::{AdminUser, User};
use rocket_db_pools::Connection;
use rocket_dyn_templates::Template;

const MAX_LISTINGS_PER_USER_PER_DAY: u32 = 10;
const MAX_UNAPPROVED_LISTINGS: u32 = 5;

#[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")]
Expand Down Expand Up @@ -73,11 +73,6 @@ async fn create_listing(
.await
.map_err(|_| "failed to update market name.")?;
let now = util::current_time_millis();
let one_day_in_ms = 24 * 60 * 60 * 1000;
let recent_listing_count =
Listing::count_for_user_since_time_ms(db, user.id(), now - one_day_in_ms)
.await
.map_err(|_| "failed to get number of recent listings.")?;

let price_sat = listing_info.price_sat.unwrap_or(0);

Expand All @@ -96,12 +91,6 @@ async fn create_listing(
if price_sat == 0 {
return Err("Price must be a positive number.".to_string());
};
if recent_listing_count >= MAX_LISTINGS_PER_USER_PER_DAY {
return Err(format!(
"More than {:?} listings in a single day not allowed.",
MAX_LISTINGS_PER_USER_PER_DAY,
));
};
if user.is_admin {
return Err("Admin user cannot create a listing.".to_string());
};
Expand All @@ -121,7 +110,7 @@ async fn create_listing(
deactivated_by_admin: false,
created_time_ms: now,
};
match Listing::insert(listing, db).await {
match Listing::insert(listing, MAX_UNAPPROVED_LISTINGS, db).await {
Ok(listing_id) => match Listing::single(db, listing_id).await {
Ok(new_listing) => Ok(new_listing.public_id),
Err(e) => {
Expand All @@ -131,7 +120,7 @@ async fn create_listing(
},
Err(e) => {
error_!("DB insertion error: {}", e);
Err("Listing could not be inserted due an internal error.".to_string())
Err(e)
}
}
}
Expand Down

0 comments on commit 97ecbf2

Please sign in to comment.