Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

171 changes: 81 additions & 90 deletions apps/labrinth/src/routes/v3/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3318,76 +3318,74 @@ pub async fn project_follow_internal(
.1;
let string = info.into_inner().0;

let result = db_models::DBProject::get(&string, &**pool, &redis)
let project = db_models::DBProject::get(&string, &**pool, &redis)
.await
.wrap_api_err("fetching project from database")?
.wrap_request_err_with(|| {
"the specified project does not exist!".to_string()
})?;

let user_id: db_ids::DBUserId = user.id.into();
let project_id: db_ids::DBProjectId = result.inner.id;
let project_id: db_ids::DBProjectId = project.inner.id;

if !is_visible_project(&result.inner, &Some(user), &pool, false)
if !is_visible_project(&project.inner, &Some(user), &pool, false)
.await
.wrap_api_err("checking project visibility")?
{
return Err(ApiError::NotFound(eyre::eyre!("resource not found")));
}

let following = sqlx::query!(
let mut transaction = pool
.begin()
.await
.wrap_internal_err("starting database transaction")?;

let insert_result = sqlx::query!(
"
SELECT EXISTS(SELECT 1 FROM mod_follows mf WHERE mf.follower_id = $1 AND mf.mod_id = $2)
",
INSERT INTO mod_follows (follower_id, mod_id)
VALUES ($1, $2)
ON CONFLICT DO NOTHING
",
user_id as db_ids::DBUserId,
project_id as db_ids::DBProjectId
)
.fetch_one(&**pool)
.await.wrap_internal_err("fetching project follow status from database")?
.exists
.unwrap_or(false);

if !following {
let mut transaction = pool
.begin()
.await
.wrap_internal_err("starting database transaction")?;
.execute(&mut transaction)
.await
.wrap_internal_err("querying database for `project_follow_internal`")?;

sqlx::query!(
"
if insert_result.rows_affected() == 0 {
return Err(ApiError::Request(eyre::eyre!(
"you are already following this project",
)));
}

sqlx::query!(
"
UPDATE mods
SET follows = follows + 1
WHERE id = $1
",
project_id as db_ids::DBProjectId,
)
.execute(&mut transaction)
.await
.wrap_internal_err("querying database for `project_follow_internal`")?;
project_id as db_ids::DBProjectId,
)
.execute(&mut transaction)
.await
.wrap_internal_err("querying database for `project_follow_internal`")?;

sqlx::query!(
"
INSERT INTO mod_follows (follower_id, mod_id)
VALUES ($1, $2)
",
user_id as db_ids::DBUserId,
project_id as db_ids::DBProjectId
)
.execute(&mut transaction)
transaction
.commit()
.await
.wrap_internal_err("querying database for `project_follow_internal`")?;
.wrap_internal_err("committing database transaction")?;

transaction
.commit()
.await
.wrap_internal_err("committing database transaction")?;
db_models::DBProject::clear_cache(
project_id,
project.inner.slug,
None,
&redis,
)
.await
.wrap_internal_err("clearing cached project data")?;

Ok(HttpResponse::NoContent().body(""))
} else {
Err(ApiError::Request(eyre::eyre!(
"You are already following this project!",
)))
}
Ok(HttpResponse::NoContent().body(""))
}

/// Unfollow a project.
Expand Down Expand Up @@ -3425,73 +3423,66 @@ pub async fn project_unfollow_internal(
.1;
let string = info.into_inner().0;

let result = db_models::DBProject::get(&string, &**pool, &redis)
let project = db_models::DBProject::get(&string, &**pool, &redis)
.await
.wrap_api_err("fetching project from database")?
.wrap_request_err_with(|| {
"the specified project does not exist!".to_string()
})?;

let user_id: db_ids::DBUserId = user.id.into();
let project_id = result.inner.id;
let project_id = project.inner.id;

let following = sqlx::query!(
let mut transaction = pool
.begin()
.await
.wrap_internal_err("starting database transaction")?;

let delete_result = sqlx::query!(
"
SELECT EXISTS(SELECT 1 FROM mod_follows mf WHERE mf.follower_id = $1 AND mf.mod_id = $2)
",
DELETE FROM mod_follows
WHERE follower_id = $1 AND mod_id = $2
",
user_id as db_ids::DBUserId,
project_id as db_ids::DBProjectId
)
.fetch_one(&**pool)
.await.wrap_internal_err("fetching project follow status from database")?
.exists
.unwrap_or(false);

if following {
let mut transaction = pool
.begin()
.await
.wrap_internal_err("starting database transaction")?;
.execute(&mut transaction)
.await
.wrap_internal_err("querying database for `project_unfollow_internal`")?;

sqlx::query!(
"
if delete_result.rows_affected() == 0 {
return Err(ApiError::Request(eyre::eyre!(
"you are not following this project",
)));
}

sqlx::query!(
"
UPDATE mods
SET follows = follows - 1
SET follows = GREATEST(follows - 1, 0)
WHERE id = $1
",
project_id as db_ids::DBProjectId,
)
.execute(&mut transaction)
.await
.wrap_internal_err(
"querying database for `project_unfollow_internal`",
)?;
project_id as db_ids::DBProjectId,
)
.execute(&mut transaction)
.await
.wrap_internal_err("querying database for `project_unfollow_internal`")?;

sqlx::query!(
"
DELETE FROM mod_follows
WHERE follower_id = $1 AND mod_id = $2
",
user_id as db_ids::DBUserId,
project_id as db_ids::DBProjectId
)
.execute(&mut transaction)
transaction
.commit()
.await
.wrap_internal_err(
"querying database for `project_unfollow_internal`",
)?;
.wrap_internal_err("committing database transaction")?;

transaction
.commit()
.await
.wrap_internal_err("committing database transaction")?;
db_models::DBProject::clear_cache(
project_id,
project.inner.slug,
None,
&redis,
)
.await
.wrap_internal_err("clearing cached project data")?;

Ok(HttpResponse::NoContent().body(""))
} else {
Err(ApiError::Request(eyre::eyre!(
"You are not following this project!",
)))
}
Ok(HttpResponse::NoContent().body(""))
}

/// Get a project's organization.
Expand Down
Loading