From 38802d3522b4eac8cf8663b0934c86b24d53f571 Mon Sep 17 00:00:00 2001 From: Geometrically <18202329+Geometrically@users.noreply.github.com> Date: Mon, 8 Mar 2021 12:52:48 -0700 Subject: [PATCH] Fix primary files, file deletion, checks for mod following, fix user following route (#175) --- sqlx-data.json | 257 ++++++++++++++++------------ src/database/models/user_item.rs | 9 +- src/database/models/version_item.rs | 2 +- src/routes/mods.rs | 114 ++++++++---- src/routes/users.rs | 20 +-- src/routes/versions.rs | 20 +-- 6 files changed, 253 insertions(+), 169 deletions(-) diff --git a/sqlx-data.json b/sqlx-data.json index 9df895abf..dd424e545 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -93,6 +93,27 @@ "nullable": [] } }, + "04e8a626bf8e210ab36f2531d63e09583fc431db7818957d69b10d966670a3e6": { + "query": "\n SELECT m.id FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE tm.user_id = $1 AND m.status = (SELECT s.id FROM statuses s WHERE s.status = $2)\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [ + false + ] + } + }, "0739834cfbef869855ed4e1aea7e1f7601f6519867ee48c573ee901c4498e04c": { "query": "\n UPDATE team_members\n SET permissions = $1\n WHERE (team_id = $2 AND user_id = $3 AND NOT role = $4)\n ", "describe": { @@ -248,6 +269,27 @@ ] } }, + "0fb1cca8a2a37107104244953371fe2f8a5e6edd57f4b325c5842c6571eb16b4": { + "query": "\n SELECT EXISTS(SELECT 1 FROM mod_follows mf WHERE mf.follower_id = $1 AND mf.mod_id = $2)\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + null + ] + } + }, "1220d15a56dbf823eaa452fbafa17442ab0568bc81a31fa38e16e3df3278e5f9": { "query": "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)", "describe": { @@ -785,18 +827,6 @@ ] } }, - "24dd644d22542e4d4493ce1e0ea57ce73e47a1a433ffcedf2631ce2f21bfd2c0": { - "query": "\n UPDATE mods\n SET follows = follows - 1\n WHERE id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - } - }, "24e5daad907eec54505274f93952d5c20f4bbdd3f771eb0a2fdfa6324768df39": { "query": "\n SELECT short, name FROM licenses\n WHERE id = $1\n ", "describe": { @@ -1198,21 +1228,20 @@ ] } }, - "3387447d6dd9c63448f021c670457270d0f2c00a270d3c1dd7160f8ed99805d3": { - "query": "\n DELETE FROM mod_follows\n WHERE follower_id = $1 AND mod_id = $2\n ", + "33fc96ac71cfa382991cfb153e89da1e9f43ebf5367c28b30c336b758222307b": { + "query": "\n DELETE FROM loaders_versions\n WHERE loaders_versions.version_id = $1\n ", "describe": { "columns": [], "parameters": { "Left": [ - "Int8", "Int8" ] }, "nullable": [] } }, - "33fc96ac71cfa382991cfb153e89da1e9f43ebf5367c28b30c336b758222307b": { - "query": "\n DELETE FROM loaders_versions\n WHERE loaders_versions.version_id = $1\n ", + "371048e45dd74c855b84cdb8a6a565ccbef5ad166ec9511ab20621c336446da6": { + "query": "\n UPDATE mods\n SET follows = follows - 1\n WHERE id = $1\n ", "describe": { "columns": [], "parameters": { @@ -1352,6 +1381,19 @@ "nullable": [] } }, + "3bdcbfa5abe43cc9b4f996f147277a7f6921cca00f82cad0ef5d85032c761a36": { + "query": "\n DELETE FROM mod_follows\n WHERE follower_id = $1 AND mod_id = $2\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [] + } + }, "3d700aaeb0d5129ac8c297ee0542757435a50a35ec94582d9d6ce67aa5302291": { "query": "\n UPDATE mods\n SET title = $1\n WHERE (id = $2)\n ", "describe": { @@ -1767,18 +1809,6 @@ ] } }, - "5322e7791400b47640f897f3e32d9a0f2915da7d71a34c4e6bf6bd648cfaaa6e": { - "query": "\n DELETE FROM files\n WHERE files.id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - } - }, "53a8966ac345cc334ad65ea907be81af74e90b1217696c7eedcf8a8e3fca736e": { "query": "\n UPDATE versions\n SET version_number = $1\n WHERE (id = $2)\n ", "describe": { @@ -2280,6 +2310,27 @@ ] } }, + "6a7b7704c2a0c52a70f5d881a1e6d3e8e77ddaa83ecc5688cd86bf327775fb76": { + "query": "\n SELECT f.id id FROM hashes h\n INNER JOIN files f ON h.file_id = f.id\n WHERE h.algorithm = $2 AND h.hash = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Bytea", + "Text" + ] + }, + "nullable": [ + false + ] + } + }, "6b28cb8b54ef57c9b6f03607611f688455f0e2b27eb5deda5a8cbc5b506b4602": { "query": "\n DELETE FROM mods\n WHERE id = $1\n ", "describe": { @@ -2332,19 +2383,6 @@ "nullable": [] } }, - "6e5ddd4069e59426636cf59a67d19cee449bc7de0762a68327181a5009118ca6": { - "query": "\n INSERT INTO mod_follows (follower_id, mod_id)\n VALUES ($1, $2)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - }, - "nullable": [] - } - }, "6f1fb4c3269b2a8190f328df025be76241eae757d9c4f3e5eb1cc01b191837df": { "query": "\n DELETE FROM mods_categories\n WHERE joining_mod_id = $1\n ", "describe": { @@ -2672,6 +2710,26 @@ ] } }, + "7d760d046292b81a88c5551ce8cde776719ce9d647f04d2928c6f6c122a8ee70": { + "query": "\n SELECT mf.mod_id FROM mod_follows mf\n WHERE mf.follower_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mod_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false + ] + } + }, "7e73d3a17807f57ba6def5ff718e6dcb3a65ef8da653d839560b24635334cf05": { "query": "\n SELECT m.title FROM mods m\n WHERE id = $1\n ", "describe": { @@ -2705,27 +2763,6 @@ "nullable": [] } }, - "80e253ea5c9846a8ce479bb2617773109a8e2583af74bfc30ddbfe3a7c5da8ae": { - "query": "\n SELECT f.id FROM files f\n INNER JOIN hashes h ON h.hash = $1 AND h.algorithm = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Bytea", - "Text" - ] - }, - "nullable": [ - false - ] - } - }, "8129255d25bf0624d83f50558b668ed7b7f9c264e380d276522fc82bc871939b": { "query": "\n INSERT INTO notifications_actions (\n notification_id, title, action_route, action_route_method\n )\n VALUES (\n $1, $2, $3, $4\n )\n ", "describe": { @@ -3951,6 +3988,19 @@ "nullable": [] } }, + "c55d2132e3e6e92dd50457affab758623dca175dc27a2d3cd4aace9cfdecf789": { + "query": "\n INSERT INTO mod_follows (follower_id, mod_id)\n VALUES ($1, $2)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [] + } + }, "c5d44333c62223bd3e68185d1fb3f95152fafec593da8d06c9b2b665218a02be": { "query": "\n UPDATE mods\n SET client_side = $1\n WHERE (id = $2)\n ", "describe": { @@ -4199,19 +4249,6 @@ ] } }, - "cc9fa2f65f62cea689ab15d4848bbb8efa77ac4a6f23440fa945847844183dd7": { - "query": "\n DELETE FROM hashes\n WHERE hash = $1 AND algorithm = $2\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bytea", - "Text" - ] - }, - "nullable": [] - } - }, "ccd913bb2f3006ffe881ce2fc4ef1e721d18fe2eed6ac62627046c955129610c": { "query": "SELECT EXISTS(SELECT 1 FROM files WHERE id=$1)", "describe": { @@ -4246,6 +4283,18 @@ "nullable": [] } }, + "cdd7f8f95c308d9474e214d584c03be0466214da1e157f6bc577b76dbef7df86": { + "query": "\n DELETE FROM hashes\n WHERE file_id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + } + }, "ce4e3569e69bb87b28af75f6f836715357b290704896edc661185e6a9c3f778e": { "query": "\n INSERT INTO mods (\n id, team_id, title, description, body,\n published, downloads, icon_url, issues_url,\n source_url, wiki_url, status, discord_url,\n client_side, server_side, license_url, license,\n slug\n )\n VALUES (\n $1, $2, $3, $4, $5,\n $6, $7, $8, $9,\n $10, $11, $12, $13,\n $14, $15, $16, $17,\n LOWER($18)\n )\n ", "describe": { @@ -4553,6 +4602,18 @@ ] } }, + "e3cc1fd070b97c4cc36bdb2f33080d4e0d7f3c3d81312d9d28a8c3c8213ad54b": { + "query": "\n DELETE FROM files\n WHERE files.id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + } + }, "e48c85a2b2e11691afae3799aa126bdd8b7338a973308bbab2760c18bb9cb0b7": { "query": "\n UPDATE versions\n SET featured = $1\n WHERE (id = $2)\n ", "describe": { @@ -4713,6 +4774,18 @@ ] } }, + "ed3e866634135d4f4c8a513eae2856ad71212f6eec09bb4ccef1506912a3a44c": { + "query": "\n UPDATE mods\n SET follows = follows + 1\n WHERE id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + } + }, "ed4c0b620d01cdcdd0c2b3b5727ae3485d51114ca76e17331cec0d244d7f972d": { "query": "\n SELECT version FROM game_versions\n ORDER BY created DESC\n ", "describe": { @@ -4731,18 +4804,6 @@ ] } }, - "ef994a6ba13ac2b24cd035d5e31302f2a53a63d1baa42d0d7ac84418a82fbb9b": { - "query": "\n UPDATE mods\n SET follows = follows + 1\n WHERE id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [] - } - }, "f0db9d8606ccc2196a9cfafe0e7090dab42bf790f25e0469b8947fac1cf043d5": { "query": "\n SELECT version FROM game_versions\n WHERE id = $1\n ", "describe": { @@ -5119,26 +5180,6 @@ ] } }, - "fdb2a6ea649bb23c69af5c756d6137e216603708ffccd4e9162fb1c9765a56aa": { - "query": "\n SELECT m.id FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE tm.user_id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false - ] - } - }, "fe73b6928f13955840e8df248688908fb6d82dd1d35dc803676639a6e0864ed5": { "query": "\n DELETE FROM downloads\n WHERE date < (CURRENT_DATE - INTERVAL '30 minutes ago')\n ", "describe": { diff --git a/src/database/models/user_item.rs b/src/database/models/user_item.rs index 564b07a2b..390d3acfe 100644 --- a/src/database/models/user_item.rs +++ b/src/database/models/user_item.rs @@ -186,7 +186,11 @@ impl User { Ok(users) } - pub async fn get_mods<'a, E>(user_id: UserId, exec: E) -> Result, sqlx::Error> + pub async fn get_mods<'a, E>( + user_id: UserId, + status: &str, + exec: E, + ) -> Result, sqlx::Error> where E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { @@ -196,9 +200,10 @@ impl User { " SELECT m.id FROM mods m INNER JOIN team_members tm ON tm.team_id = m.team_id - WHERE tm.user_id = $1 + WHERE tm.user_id = $1 AND m.status = (SELECT s.id FROM statuses s WHERE s.status = $2) ", user_id as UserId, + status, ) .fetch_many(exec) .try_filter_map(|e| async { Ok(e.right().map(|m| ModId(m.id))) }) diff --git a/src/database/models/version_item.rs b/src/database/models/version_item.rs index 5ccce5353..69389ca77 100644 --- a/src/database/models/version_item.rs +++ b/src/database/models/version_item.rs @@ -543,7 +543,7 @@ impl Version { url: file[3].to_string(), filename: file[1].to_string(), hashes: file_hashes, - primary: file[3].parse().unwrap_or(false), + primary: file[2].parse().unwrap_or(false), }) } }); diff --git a/src/routes/mods.rs b/src/routes/mods.rs index cf46e7488..59d12d6ba 100644 --- a/src/routes/mods.rs +++ b/src/routes/mods.rs @@ -8,7 +8,7 @@ use crate::routes::ApiError; use crate::search::indexing::queue::CreationQueue; use crate::search::{search_for_mod, SearchConfig, SearchError}; use actix_web::web::Data; -use actix_web::{delete, get, patch, web, HttpRequest, HttpResponse}; +use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse}; use futures::StreamExt; use serde::{Deserialize, Serialize}; use sqlx::PgPool; @@ -986,7 +986,7 @@ pub async fn mod_delete( } } -#[get("{id}/follow")] +#[post("{id}/follow")] pub async fn mod_follow( req: HttpRequest, info: web::Path<(models::ids::ModId,)>, @@ -1003,31 +1003,50 @@ pub async fn mod_follow( let user_id: database::models::ids::UserId = user.id.into(); let mod_id: database::models::ids::ModId = id.into(); - sqlx::query!( + let following = sqlx::query!( " - UPDATE mods - SET follows = follows + 1 - WHERE id = $1 - ", - mod_id as database::models::ids::ModId, - ) - .execute(&**pool) - .await - .map_err(|e| ApiError::DatabaseError(e.into()))?; - - sqlx::query!( - " - INSERT INTO mod_follows (follower_id, mod_id) - VALUES ($1, $2) + SELECT EXISTS(SELECT 1 FROM mod_follows mf WHERE mf.follower_id = $1 AND mf.mod_id = $2) ", user_id as database::models::ids::UserId, mod_id as database::models::ids::ModId ) - .execute(&**pool) + .fetch_one(&**pool) .await - .map_err(|e| ApiError::DatabaseError(e.into()))?; + .map_err(|e| ApiError::DatabaseError(e.into()))? + .exists + .unwrap_or(false); - Ok(HttpResponse::Ok().body("")) + if !following { + sqlx::query!( + " + UPDATE mods + SET follows = follows + 1 + WHERE id = $1 + ", + mod_id as database::models::ids::ModId, + ) + .execute(&**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + sqlx::query!( + " + INSERT INTO mod_follows (follower_id, mod_id) + VALUES ($1, $2) + ", + user_id as database::models::ids::UserId, + mod_id as database::models::ids::ModId + ) + .execute(&**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + Ok(HttpResponse::Ok().body("")) + } else { + Err(ApiError::InvalidInputError( + "You are already following this mod!".to_string(), + )) + } } #[delete("{id}/follow")] @@ -1042,31 +1061,50 @@ pub async fn mod_unfollow( let user_id: database::models::ids::UserId = user.id.into(); let mod_id: database::models::ids::ModId = id.into(); - sqlx::query!( + let following = sqlx::query!( " - UPDATE mods - SET follows = follows - 1 - WHERE id = $1 - ", - mod_id as database::models::ids::ModId, - ) - .execute(&**pool) - .await - .map_err(|e| ApiError::DatabaseError(e.into()))?; - - sqlx::query!( - " - DELETE FROM mod_follows - WHERE follower_id = $1 AND mod_id = $2 + SELECT EXISTS(SELECT 1 FROM mod_follows mf WHERE mf.follower_id = $1 AND mf.mod_id = $2) ", user_id as database::models::ids::UserId, mod_id as database::models::ids::ModId ) - .execute(&**pool) + .fetch_one(&**pool) .await - .map_err(|e| ApiError::DatabaseError(e.into()))?; + .map_err(|e| ApiError::DatabaseError(e.into()))? + .exists + .unwrap_or(false); - Ok(HttpResponse::Ok().body("")) + if following { + sqlx::query!( + " + UPDATE mods + SET follows = follows - 1 + WHERE id = $1 + ", + mod_id as database::models::ids::ModId, + ) + .execute(&**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + sqlx::query!( + " + DELETE FROM mod_follows + WHERE follower_id = $1 AND mod_id = $2 + ", + user_id as database::models::ids::UserId, + mod_id as database::models::ids::ModId + ) + .execute(&**pool) + .await + .map_err(|e| ApiError::DatabaseError(e.into()))?; + + Ok(HttpResponse::Ok().body("")) + } else { + Err(ApiError::InvalidInputError( + "You are not following this mod!".to_string(), + )) + } } pub async fn delete_from_index( diff --git a/src/routes/users.rs b/src/routes/users.rs index 0b38e1543..a9195dacf 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -1,7 +1,8 @@ use crate::auth::get_user_from_headers; use crate::database::models::User; use crate::file_hosting::FileHost; -use crate::models::ids::NotificationId; +use crate::models::ids::ModId; +use crate::models::mods::ModStatus; use crate::models::notifications::Notification; use crate::models::users::{Role, UserId}; use crate::routes::notifications::convert_notification; @@ -136,7 +137,7 @@ pub async fn mods_list( .exists; if user_exists.unwrap_or(false) { - let mod_data = User::get_mods(id, &**pool) + let mod_data = User::get_mods(id, ModStatus::Approved.as_str(), &**pool) .await .map_err(|e| ApiError::DatabaseError(e.into()))?; @@ -371,7 +372,6 @@ pub async fn user_icon_edit( .execute(&**pool) .await .map_err(|e| ApiError::DatabaseError(e.into()))?; - Ok(HttpResponse::Ok().body("")) } else { Err(ApiError::InvalidInputError(format!( @@ -443,20 +443,20 @@ pub async fn user_follows( use futures::TryStreamExt; let user_id: crate::database::models::UserId = id.into(); - let notifications: Vec = sqlx::query!( + let mods: Vec = sqlx::query!( " - SELECT n.id FROM notifications n - WHERE n.user_id = $1 - ", + SELECT mf.mod_id FROM mod_follows mf + WHERE mf.follower_id = $1 + ", user_id as crate::database::models::ids::UserId, ) .fetch_many(&**pool) - .try_filter_map(|e| async { Ok(e.right().map(|m| NotificationId(m.id as u64))) }) - .try_collect::>() + .try_filter_map(|e| async { Ok(e.right().map(|m| ModId(m.mod_id as u64))) }) + .try_collect::>() .await .map_err(|e| ApiError::DatabaseError(e.into()))?; - Ok(HttpResponse::Ok().json(notifications)) + Ok(HttpResponse::Ok().json(mods)) } #[get("{id}/notifications")] diff --git a/src/routes/versions.rs b/src/routes/versions.rs index 416b62cd1..05f44c516 100644 --- a/src/routes/versions.rs +++ b/src/routes/versions.rs @@ -450,8 +450,9 @@ pub async fn version_edit( if let Some(primary_file) = &new_version.primary_file { let result = sqlx::query!( " - SELECT f.id FROM files f - INNER JOIN hashes h ON h.hash = $1 AND h.algorithm = $2 + SELECT f.id id FROM hashes h + INNER JOIN files f ON h.file_id = f.id + WHERE h.algorithm = $2 AND h.hash = $1 ", primary_file.1.as_bytes(), primary_file.0 @@ -779,11 +780,10 @@ pub async fn delete_file( sqlx::query!( " - DELETE FROM hashes - WHERE hash = $1 AND algorithm = $2 - ", - hash.as_bytes(), - algorithm.algorithm + DELETE FROM hashes + WHERE file_id = $1 + ", + row.id ) .execute(&mut *transaction) .await @@ -791,9 +791,9 @@ pub async fn delete_file( sqlx::query!( " - DELETE FROM files - WHERE files.id = $1 - ", + DELETE FROM files + WHERE files.id = $1 + ", row.id, ) .execute(&mut *transaction)