From 630a71c46c83a24993ef09ea59d2b46c8888200e Mon Sep 17 00:00:00 2001 From: Geometrically <18202329+Geometrically@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:48:46 -0700 Subject: [PATCH] Queue Dates + Warnings, some cleanup (#549) * Queue Dates + Warnings, some cleanup * Fix ping * Fix repeated discord messaging * Fix compile error + run fmt --- migrations/20230221212958_queue-date.sql | 2 + sqlx-data.json | 2084 ++++++---------------- src/database/models/notification_item.rs | 68 +- src/database/models/project_item.rs | 200 +-- src/database/models/report_item.rs | 33 +- src/database/models/team_item.rs | 102 +- src/database/models/user_item.rs | 51 +- src/database/models/version_item.rs | 191 +- src/main.rs | 69 + src/models/projects.rs | 3 + src/routes/moderation.rs | 2 +- src/routes/notifications.rs | 8 +- src/routes/project_creation.rs | 2 + src/routes/projects.rs | 31 +- src/routes/reports.rs | 3 +- src/routes/teams.rs | 4 +- src/routes/updates.rs | 2 +- src/routes/users.rs | 4 +- src/routes/v1/users.rs | 2 +- src/routes/v1/versions.rs | 6 +- src/routes/version_file.rs | 37 +- src/routes/versions.rs | 6 +- src/util/webhook.rs | 3 + 23 files changed, 701 insertions(+), 2212 deletions(-) create mode 100644 migrations/20230221212958_queue-date.sql diff --git a/migrations/20230221212958_queue-date.sql b/migrations/20230221212958_queue-date.sql new file mode 100644 index 000000000..df051f33d --- /dev/null +++ b/migrations/20230221212958_queue-date.sql @@ -0,0 +1,2 @@ +-- Add migration script here +ALTER TABLE mods ADD COLUMN queued timestamptz NULL \ No newline at end of file diff --git a/sqlx-data.json b/sqlx-data.json index df414d3ca..adb3f8a08 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -1,5 +1,17 @@ { "db": "PostgreSQL", + "010cafcafb6adc25b00e3c81d844736b0245e752a90334c58209d8a02536c800": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Int8" + ] + } + }, + "query": "\n UPDATE mods\n SET moderation_message = NULL, moderation_message_body = NULL, queued = NOW()\n WHERE (id = $1)\n " + }, "0267d1ea5387d4acfc132aeb4776004a1ebb048e7789e686bfaba3357d392f62": { "describe": { "columns": [], @@ -118,18 +130,6 @@ }, "query": "\n UPDATE mods\n SET source_url = $1\n WHERE (id = $2)\n " }, - "04dcb2565608e296502694efc0c59bc77c41175ef65c830f2fef745773f18c86": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n UPDATE mods\n SET moderation_message_body = NULL\n WHERE (id = $1)\n " - }, "04e5ecb14c526000e9098efb65861f6125e6fcc88f39d6ad811ac8504d229de1": { "describe": { "columns": [], @@ -425,6 +425,26 @@ }, "query": "\n SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)\n " }, + "0f6469055265ad8b114136368001aa927b587df9f64f0e19fd37d1f4b4adab60": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n SELECT id FROM mods\n WHERE status = $1 AND queued < NOW() - INTERVAL '1 day'\n ORDER BY updated ASC\n " + }, "0fb1cca8a2a37107104244953371fe2f8a5e6edd57f4b325c5842c6571eb16b4": { "describe": { "columns": [ @@ -508,6 +528,212 @@ }, "query": "\n UPDATE mods\n SET webhook_sent = TRUE\n WHERE id = $1\n " }, + "154f8fdf00fc9905d8e2cd339687c63a523de7e19f6072457cfe723600fb1690": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "project_type", + "ordinal": 1, + "type_info": "Int4" + }, + { + "name": "title", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "description", + "ordinal": 3, + "type_info": "Varchar" + }, + { + "name": "downloads", + "ordinal": 4, + "type_info": "Int4" + }, + { + "name": "follows", + "ordinal": 5, + "type_info": "Int4" + }, + { + "name": "icon_url", + "ordinal": 6, + "type_info": "Varchar" + }, + { + "name": "body", + "ordinal": 7, + "type_info": "Varchar" + }, + { + "name": "published", + "ordinal": 8, + "type_info": "Timestamptz" + }, + { + "name": "updated", + "ordinal": 9, + "type_info": "Timestamptz" + }, + { + "name": "approved", + "ordinal": 10, + "type_info": "Timestamptz" + }, + { + "name": "queued", + "ordinal": 11, + "type_info": "Timestamptz" + }, + { + "name": "status", + "ordinal": 12, + "type_info": "Varchar" + }, + { + "name": "requested_status", + "ordinal": 13, + "type_info": "Varchar" + }, + { + "name": "issues_url", + "ordinal": 14, + "type_info": "Varchar" + }, + { + "name": "source_url", + "ordinal": 15, + "type_info": "Varchar" + }, + { + "name": "wiki_url", + "ordinal": 16, + "type_info": "Varchar" + }, + { + "name": "discord_url", + "ordinal": 17, + "type_info": "Varchar" + }, + { + "name": "license_url", + "ordinal": 18, + "type_info": "Varchar" + }, + { + "name": "team_id", + "ordinal": 19, + "type_info": "Int8" + }, + { + "name": "client_side", + "ordinal": 20, + "type_info": "Int4" + }, + { + "name": "server_side", + "ordinal": 21, + "type_info": "Int4" + }, + { + "name": "license", + "ordinal": 22, + "type_info": "Varchar" + }, + { + "name": "slug", + "ordinal": 23, + "type_info": "Varchar" + }, + { + "name": "moderation_message", + "ordinal": 24, + "type_info": "Varchar" + }, + { + "name": "moderation_message_body", + "ordinal": 25, + "type_info": "Varchar" + }, + { + "name": "flame_anvil_project", + "ordinal": 26, + "type_info": "Int4" + }, + { + "name": "flame_anvil_user", + "ordinal": 27, + "type_info": "Int8" + }, + { + "name": "webhook_sent", + "ordinal": 28, + "type_info": "Bool" + }, + { + "name": "color", + "ordinal": 29, + "type_info": "Int4" + }, + { + "name": "loaders", + "ordinal": 30, + "type_info": "VarcharArray" + }, + { + "name": "game_versions", + "ordinal": 31, + "type_info": "VarcharArray" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + true, + true, + false, + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + true, + true, + true, + true, + true, + false, + true, + false, + false + ], + "parameters": { + "Left": [ + "Int8Array" + ] + } + }, + "query": "\n SELECT id, project_type, title, description, downloads, follows,\n icon_url, body, published,\n updated, approved, queued, status, requested_status,\n issues_url, source_url, wiki_url, discord_url, license_url,\n team_id, client_side, server_side, license, slug,\n moderation_message, moderation_message_body, flame_anvil_project,\n flame_anvil_user, webhook_sent, color, loaders, game_versions\n FROM mods\n WHERE id = ANY($1)\n " + }, "15b8ea323c2f6d03c2e385d9c46d7f13460764f2f106fd638226c42ae0217f75": { "describe": { "columns": [], @@ -1229,255 +1455,6 @@ }, "query": "\n SELECT id FROM side_types\n WHERE name = $1\n " }, - "1ed4227372b608bae9a621d4197bfa825249364cc37cc9dd812cbd72bdc4b1d8": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "project_type", - "ordinal": 1, - "type_info": "Int4" - }, - { - "name": "title", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "description", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "downloads", - "ordinal": 4, - "type_info": "Int4" - }, - { - "name": "follows", - "ordinal": 5, - "type_info": "Int4" - }, - { - "name": "icon_url", - "ordinal": 6, - "type_info": "Varchar" - }, - { - "name": "body", - "ordinal": 7, - "type_info": "Varchar" - }, - { - "name": "published", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "updated", - "ordinal": 9, - "type_info": "Timestamptz" - }, - { - "name": "approved", - "ordinal": 10, - "type_info": "Timestamptz" - }, - { - "name": "status", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "requested_status", - "ordinal": 12, - "type_info": "Varchar" - }, - { - "name": "issues_url", - "ordinal": 13, - "type_info": "Varchar" - }, - { - "name": "source_url", - "ordinal": 14, - "type_info": "Varchar" - }, - { - "name": "wiki_url", - "ordinal": 15, - "type_info": "Varchar" - }, - { - "name": "discord_url", - "ordinal": 16, - "type_info": "Varchar" - }, - { - "name": "license_url", - "ordinal": 17, - "type_info": "Varchar" - }, - { - "name": "team_id", - "ordinal": 18, - "type_info": "Int8" - }, - { - "name": "client_side", - "ordinal": 19, - "type_info": "Int4" - }, - { - "name": "server_side", - "ordinal": 20, - "type_info": "Int4" - }, - { - "name": "license", - "ordinal": 21, - "type_info": "Varchar" - }, - { - "name": "slug", - "ordinal": 22, - "type_info": "Varchar" - }, - { - "name": "moderation_message", - "ordinal": 23, - "type_info": "Varchar" - }, - { - "name": "moderation_message_body", - "ordinal": 24, - "type_info": "Varchar" - }, - { - "name": "client_side_type", - "ordinal": 25, - "type_info": "Varchar" - }, - { - "name": "server_side_type", - "ordinal": 26, - "type_info": "Varchar" - }, - { - "name": "project_type_name", - "ordinal": 27, - "type_info": "Varchar" - }, - { - "name": "flame_anvil_project", - "ordinal": 28, - "type_info": "Int4" - }, - { - "name": "flame_anvil_user", - "ordinal": 29, - "type_info": "Int8" - }, - { - "name": "webhook_sent", - "ordinal": 30, - "type_info": "Bool" - }, - { - "name": "color", - "ordinal": 31, - "type_info": "Int4" - }, - { - "name": "loaders", - "ordinal": 32, - "type_info": "VarcharArray" - }, - { - "name": "game_versions", - "ordinal": 33, - "type_info": "VarcharArray" - }, - { - "name": "categories", - "ordinal": 34, - "type_info": "VarcharArray" - }, - { - "name": "additional_categories", - "ordinal": 35, - "type_info": "VarcharArray" - }, - { - "name": "versions", - "ordinal": 36, - "type_info": "Jsonb" - }, - { - "name": "gallery", - "ordinal": 37, - "type_info": "Jsonb" - }, - { - "name": "donations", - "ordinal": 38, - "type_info": "Jsonb" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - false, - false, - false, - false, - true, - true, - true, - false, - false, - false, - true, - true, - false, - true, - false, - false, - null, - null, - null, - null, - null - ], - "parameters": { - "Left": [ - "Int8", - "TextArray" - ] - } - }, - "query": "\n SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.body body, m.published published,\n m.updated updated, m.approved approved, m.status status, m.requested_status requested_status,\n m.issues_url issues_url, m.source_url source_url, m.wiki_url wiki_url, m.discord_url discord_url, m.license_url license_url,\n m.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body,\n cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user, m.webhook_sent webhook_sent, m.color,\n m.loaders loaders, m.game_versions game_versions,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,\n JSONB_AGG(DISTINCT jsonb_build_object('id', v.id, 'date_published', v.date_published)) filter (where v.id is not null) versions,\n JSONB_AGG(DISTINCT jsonb_build_object('image_url', mg.image_url, 'featured', mg.featured, 'title', mg.title, 'description', mg.description, 'created', mg.created, 'ordering', mg.ordering)) filter (where mg.image_url is not null) gallery,\n JSONB_AGG(DISTINCT jsonb_build_object('platform_id', md.joining_platform_id, 'platform_short', dp.short, 'platform_name', dp.name,'url', md.url)) filter (where md.joining_platform_id is not null) donations\n FROM mods m\n INNER JOIN project_types pt ON pt.id = m.project_type\n INNER JOIN side_types cs ON m.client_side = cs.id\n INNER JOIN side_types ss ON m.server_side = ss.id\n LEFT JOIN mods_donations md ON md.joining_mod_id = m.id\n LEFT JOIN donation_platforms dp ON md.joining_platform_id = dp.id\n LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id\n LEFT JOIN categories c ON mc.joining_category_id = c.id\n LEFT JOIN versions v ON v.mod_id = m.id AND v.status = ANY($2)\n LEFT JOIN mods_gallery mg ON mg.mod_id = m.id\n WHERE m.id = $1\n GROUP BY pt.id, cs.id, ss.id, m.id;\n " - }, "1ffce9b2d5c9fa6c8b9abce4bad9f9419c44ad6367b7463b979c91b9b5b4fea1": { "describe": { "columns": [ @@ -1676,255 +1653,6 @@ }, "query": "\n SELECT m.id id, tm.user_id user_id, tm.payouts_split payouts_split\n FROM mods m\n INNER JOIN team_members tm on m.team_id = tm.team_id AND tm.accepted = TRUE\n WHERE m.id = ANY($1)\n " }, - "258fcfa74540b5a9c65df294b74b5306234ece346ce72216eea68d93fff7f2e4": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "project_type", - "ordinal": 1, - "type_info": "Int4" - }, - { - "name": "title", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "description", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "downloads", - "ordinal": 4, - "type_info": "Int4" - }, - { - "name": "follows", - "ordinal": 5, - "type_info": "Int4" - }, - { - "name": "icon_url", - "ordinal": 6, - "type_info": "Varchar" - }, - { - "name": "body", - "ordinal": 7, - "type_info": "Varchar" - }, - { - "name": "published", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "updated", - "ordinal": 9, - "type_info": "Timestamptz" - }, - { - "name": "approved", - "ordinal": 10, - "type_info": "Timestamptz" - }, - { - "name": "status", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "requested_status", - "ordinal": 12, - "type_info": "Varchar" - }, - { - "name": "issues_url", - "ordinal": 13, - "type_info": "Varchar" - }, - { - "name": "source_url", - "ordinal": 14, - "type_info": "Varchar" - }, - { - "name": "wiki_url", - "ordinal": 15, - "type_info": "Varchar" - }, - { - "name": "discord_url", - "ordinal": 16, - "type_info": "Varchar" - }, - { - "name": "license_url", - "ordinal": 17, - "type_info": "Varchar" - }, - { - "name": "team_id", - "ordinal": 18, - "type_info": "Int8" - }, - { - "name": "client_side", - "ordinal": 19, - "type_info": "Int4" - }, - { - "name": "server_side", - "ordinal": 20, - "type_info": "Int4" - }, - { - "name": "license", - "ordinal": 21, - "type_info": "Varchar" - }, - { - "name": "slug", - "ordinal": 22, - "type_info": "Varchar" - }, - { - "name": "moderation_message", - "ordinal": 23, - "type_info": "Varchar" - }, - { - "name": "moderation_message_body", - "ordinal": 24, - "type_info": "Varchar" - }, - { - "name": "client_side_type", - "ordinal": 25, - "type_info": "Varchar" - }, - { - "name": "server_side_type", - "ordinal": 26, - "type_info": "Varchar" - }, - { - "name": "project_type_name", - "ordinal": 27, - "type_info": "Varchar" - }, - { - "name": "flame_anvil_project", - "ordinal": 28, - "type_info": "Int4" - }, - { - "name": "flame_anvil_user", - "ordinal": 29, - "type_info": "Int8" - }, - { - "name": "webhook_sent", - "ordinal": 30, - "type_info": "Bool" - }, - { - "name": "color", - "ordinal": 31, - "type_info": "Int4" - }, - { - "name": "loaders", - "ordinal": 32, - "type_info": "VarcharArray" - }, - { - "name": "game_versions", - "ordinal": 33, - "type_info": "VarcharArray" - }, - { - "name": "categories", - "ordinal": 34, - "type_info": "VarcharArray" - }, - { - "name": "additional_categories", - "ordinal": 35, - "type_info": "VarcharArray" - }, - { - "name": "versions", - "ordinal": 36, - "type_info": "Jsonb" - }, - { - "name": "gallery", - "ordinal": 37, - "type_info": "Jsonb" - }, - { - "name": "donations", - "ordinal": 38, - "type_info": "Jsonb" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - false, - false, - false, - false, - true, - true, - true, - false, - false, - false, - true, - true, - false, - true, - false, - false, - null, - null, - null, - null, - null - ], - "parameters": { - "Left": [ - "Int8Array", - "TextArray" - ] - } - }, - "query": "\n SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.body body, m.published published,\n m.updated updated, m.approved approved, m.status status, m.requested_status requested_status,\n m.issues_url issues_url, m.source_url source_url, m.wiki_url wiki_url, m.discord_url discord_url, m.license_url license_url,\n m.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body,\n cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user, m.webhook_sent, m.color,\n m.loaders loaders, m.game_versions game_versions,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,\n JSONB_AGG(DISTINCT jsonb_build_object('id', v.id, 'date_published', v.date_published)) filter (where v.id is not null) versions,\n JSONB_AGG(DISTINCT jsonb_build_object('image_url', mg.image_url, 'featured', mg.featured, 'title', mg.title, 'description', mg.description, 'created', mg.created, 'ordering', mg.ordering)) filter (where mg.image_url is not null) gallery,\n JSONB_AGG(DISTINCT jsonb_build_object('platform_id', md.joining_platform_id, 'platform_short', dp.short, 'platform_name', dp.name,'url', md.url)) filter (where md.joining_platform_id is not null) donations\n FROM mods m\n INNER JOIN project_types pt ON pt.id = m.project_type\n INNER JOIN side_types cs ON m.client_side = cs.id\n INNER JOIN side_types ss ON m.server_side = ss.id\n LEFT JOIN mods_donations md ON md.joining_mod_id = m.id\n LEFT JOIN donation_platforms dp ON md.joining_platform_id = dp.id\n LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id\n LEFT JOIN categories c ON mc.joining_category_id = c.id\n LEFT JOIN versions v ON v.mod_id = m.id AND v.status = ANY($2)\n LEFT JOIN mods_gallery mg ON mg.mod_id = m.id\n WHERE m.id = ANY($1)\n GROUP BY pt.id, cs.id, ss.id, m.id;\n " - }, "27a35fca63dfc3801f95958604f0ac27afd81800e2dc981382d6f923c4415d32": { "describe": { "columns": [], @@ -1949,122 +1677,6 @@ }, "query": "INSERT INTO banned_users (github_id) VALUES ($1);" }, - "28e5a9c09fac55677fea16d21482a626b7c5f8edbf2af182ed67e44a793ef2e0": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "mod_id", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "author_id", - "ordinal": 2, - "type_info": "Int8" - }, - { - "name": "version_name", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "version_number", - "ordinal": 4, - "type_info": "Varchar" - }, - { - "name": "changelog", - "ordinal": 5, - "type_info": "Varchar" - }, - { - "name": "date_published", - "ordinal": 6, - "type_info": "Timestamptz" - }, - { - "name": "downloads", - "ordinal": 7, - "type_info": "Int4" - }, - { - "name": "version_type", - "ordinal": 8, - "type_info": "Varchar" - }, - { - "name": "featured", - "ordinal": 9, - "type_info": "Bool" - }, - { - "name": "status", - "ordinal": 10, - "type_info": "Varchar" - }, - { - "name": "requested_status", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "game_versions", - "ordinal": 12, - "type_info": "Jsonb" - }, - { - "name": "loaders", - "ordinal": 13, - "type_info": "VarcharArray" - }, - { - "name": "files", - "ordinal": 14, - "type_info": "Jsonb" - }, - { - "name": "hashes", - "ordinal": 15, - "type_info": "Jsonb" - }, - { - "name": "dependencies", - "ordinal": 16, - "type_info": "Jsonb" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - true, - null, - null, - null, - null, - null - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT v.id id, v.mod_id mod_id, v.author_id author_id, v.name version_name, v.version_number version_number,\n v.changelog changelog, v.date_published date_published, v.downloads downloads,\n v.version_type version_type, v.featured featured, v.status status, v.requested_status requested_status,\n JSONB_AGG(DISTINCT jsonb_build_object('version', gv.version, 'created', gv.created)) filter (where gv.version is not null) game_versions,\n ARRAY_AGG(DISTINCT l.loader) filter (where l.loader is not null) loaders,\n JSONB_AGG(DISTINCT jsonb_build_object('id', f.id, 'url', f.url, 'filename', f.filename, 'primary', f.is_primary, 'size', f.size, 'file_type', f.file_type)) filter (where f.id is not null) files,\n JSONB_AGG(DISTINCT jsonb_build_object('algorithm', h.algorithm, 'hash', encode(h.hash, 'escape'), 'file_id', h.file_id)) filter (where h.hash is not null) hashes,\n JSONB_AGG(DISTINCT jsonb_build_object('project_id', d.mod_dependency_id, 'version_id', d.dependency_id, 'dependency_type', d.dependency_type,'file_name', dependency_file_name)) filter (where d.dependency_type is not null) dependencies\n FROM versions v\n LEFT OUTER JOIN game_versions_versions gvv on v.id = gvv.joining_version_id\n LEFT OUTER JOIN game_versions gv on gvv.game_version_id = gv.id\n LEFT OUTER JOIN loaders_versions lv on v.id = lv.version_id\n LEFT OUTER JOIN loaders l on lv.loader_id = l.id\n LEFT OUTER JOIN files f on v.id = f.version_id\n LEFT OUTER JOIN hashes h on f.id = h.file_id\n LEFT OUTER JOIN dependencies d on v.id = d.dependent_id\n WHERE v.id = $1\n GROUP BY v.id;\n " - }, "299b8ea6e7a0048fa389cc4432715dc2a09e227d2f08e91167a43372a7ac6e35": { "describe": { "columns": [], @@ -2242,206 +1854,6 @@ }, "query": "\n SELECT COUNT(v.id)\n FROM versions v\n INNER JOIN mods m on v.mod_id = m.id AND m.status = ANY($1)\n WHERE v.status = ANY($2)\n " }, - "2e7d9e595d5187abcdddaccba20b3797baee649cfa52c5662953a72efcae4c73": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "project_type", - "ordinal": 1, - "type_info": "Int4" - }, - { - "name": "title", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "description", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "downloads", - "ordinal": 4, - "type_info": "Int4" - }, - { - "name": "follows", - "ordinal": 5, - "type_info": "Int4" - }, - { - "name": "icon_url", - "ordinal": 6, - "type_info": "Varchar" - }, - { - "name": "body", - "ordinal": 7, - "type_info": "Varchar" - }, - { - "name": "published", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "updated", - "ordinal": 9, - "type_info": "Timestamptz" - }, - { - "name": "approved", - "ordinal": 10, - "type_info": "Timestamptz" - }, - { - "name": "status", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "requested_status", - "ordinal": 12, - "type_info": "Varchar" - }, - { - "name": "issues_url", - "ordinal": 13, - "type_info": "Varchar" - }, - { - "name": "source_url", - "ordinal": 14, - "type_info": "Varchar" - }, - { - "name": "wiki_url", - "ordinal": 15, - "type_info": "Varchar" - }, - { - "name": "discord_url", - "ordinal": 16, - "type_info": "Varchar" - }, - { - "name": "license_url", - "ordinal": 17, - "type_info": "Varchar" - }, - { - "name": "team_id", - "ordinal": 18, - "type_info": "Int8" - }, - { - "name": "client_side", - "ordinal": 19, - "type_info": "Int4" - }, - { - "name": "server_side", - "ordinal": 20, - "type_info": "Int4" - }, - { - "name": "license", - "ordinal": 21, - "type_info": "Varchar" - }, - { - "name": "slug", - "ordinal": 22, - "type_info": "Varchar" - }, - { - "name": "moderation_message", - "ordinal": 23, - "type_info": "Varchar" - }, - { - "name": "moderation_message_body", - "ordinal": 24, - "type_info": "Varchar" - }, - { - "name": "flame_anvil_project", - "ordinal": 25, - "type_info": "Int4" - }, - { - "name": "flame_anvil_user", - "ordinal": 26, - "type_info": "Int8" - }, - { - "name": "webhook_sent", - "ordinal": 27, - "type_info": "Bool" - }, - { - "name": "color", - "ordinal": 28, - "type_info": "Int4" - }, - { - "name": "loaders", - "ordinal": 29, - "type_info": "VarcharArray" - }, - { - "name": "game_versions", - "ordinal": 30, - "type_info": "VarcharArray" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - false, - false, - false, - false, - true, - true, - true, - true, - true, - false, - true, - false, - false - ], - "parameters": { - "Left": [ - "Int8Array" - ] - } - }, - "query": "\n SELECT id, project_type, title, description, downloads, follows,\n icon_url, body, published,\n updated, approved, status, requested_status,\n issues_url, source_url, wiki_url, discord_url, license_url,\n team_id, client_side, server_side, license, slug,\n moderation_message, moderation_message_body, flame_anvil_project,\n flame_anvil_user, webhook_sent, color, loaders, game_versions\n FROM mods\n WHERE id = ANY($1)\n " - }, "2f7c011654d15c85dbb614ac01ed5613a6872ea8c172ab38fdaa0eb38a7d6e4f": { "describe": { "columns": [], @@ -2556,6 +1968,27 @@ }, "query": "\n UPDATE mods_gallery\n SET featured = $2\n WHERE id = $1\n " }, + "3baabc9f08401801fa290866888c540746fc50c1d79911f08f3322b605ce5c30": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + } + }, + "query": "\n SELECT id FROM mods\n WHERE status = $1\n ORDER BY queued ASC\n LIMIT $2;\n " + }, "3bdcbfa5abe43cc9b4f996f147277a7f6921cca00f82cad0ef5d85032c761a36": { "describe": { "columns": [], @@ -3227,18 +2660,6 @@ }, "query": "\n UPDATE versions\n SET version_number = $1\n WHERE (id = $2)\n " }, - "57a38641fe5bdb273190e8d586f46284340b9ff11b6ae3177923631a37bb11eb": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n UPDATE mods\n SET moderation_message = NULL\n WHERE (id = $1)\n " - }, "57bb3db92e6a8fb8606005be955e2379f13a04f101f91358322a591a860a7f9e": { "describe": { "columns": [ @@ -3309,62 +2730,6 @@ }, "query": "\n DELETE FROM reports\n WHERE version_id = $1\n " }, - "5ad1f23da1b6f0f613de3412b928d2677a0359111dab4174e69ef6b0ef78202b": { - "describe": { - "columns": [ - { - "name": "name", - "ordinal": 0, - "type_info": "Varchar" - }, - { - "name": "mod_id", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "version_id", - "ordinal": 2, - "type_info": "Int8" - }, - { - "name": "user_id", - "ordinal": 3, - "type_info": "Int8" - }, - { - "name": "body", - "ordinal": 4, - "type_info": "Varchar" - }, - { - "name": "reporter", - "ordinal": 5, - "type_info": "Int8" - }, - { - "name": "created", - "ordinal": 6, - "type_info": "Timestamptz" - } - ], - "nullable": [ - false, - true, - true, - true, - false, - false, - false - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT rt.name, r.mod_id, r.version_id, r.user_id, r.body, r.reporter, r.created\n FROM reports r\n INNER JOIN report_types rt ON rt.id = r.report_type_id\n WHERE r.id = $1\n " - }, "5c3b340d278c356b6bc2cd7110e5093a7d1ad982ae0f468f8fff7c54e4e6603a": { "describe": { "columns": [ @@ -4067,86 +3432,6 @@ }, "query": "SELECT EXISTS(SELECT 1 FROM team_members WHERE team_id = $1 AND user_id = $2)" }, - "79848ca51533ae6e70ff46986f1fd3a69d1ce4aa88a20541af7f347b19bf95d9": { - "describe": { - "columns": [ - { - "name": "mod_id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "author_id", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "name", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "version_number", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "changelog", - "ordinal": 4, - "type_info": "Varchar" - }, - { - "name": "date_published", - "ordinal": 5, - "type_info": "Timestamptz" - }, - { - "name": "downloads", - "ordinal": 6, - "type_info": "Int4" - }, - { - "name": "version_type", - "ordinal": 7, - "type_info": "Varchar" - }, - { - "name": "featured", - "ordinal": 8, - "type_info": "Bool" - }, - { - "name": "status", - "ordinal": 9, - "type_info": "Varchar" - }, - { - "name": "requested_status", - "ordinal": 10, - "type_info": "Varchar" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - true - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT v.mod_id, v.author_id, v.name, v.version_number,\n v.changelog, v.date_published, v.downloads,\n v.version_type, v.featured, v.status, v.requested_status\n FROM versions v\n WHERE v.id = $1\n " - }, "79b896b1a8ddab285294638302976b75d0d915f36036383cc21bd2fc48d4502c": { "describe": { "columns": [], @@ -4215,27 +3500,6 @@ }, "query": "\n SELECT SUM(pv.amount) amount\n FROM payouts_values pv\n WHERE pv.user_id = $1 AND created > NOW() - '1 month'::interval\n " }, - "7c34e28fc3090f5a1c7c55d59905d28f41c1e22dc682cc7c54b9234fc7212e99": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - } - ], - "nullable": [ - false - ], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - } - }, - "query": "\n SELECT id FROM mods\n WHERE status = $1\n ORDER BY updated ASC\n LIMIT $2;\n " - }, "7c61fee015231f0a97c25d24f2c6be24821e39e330ab82344ad3b985d0d2aaea": { "describe": { "columns": [ @@ -4269,257 +3533,6 @@ }, "query": "\n INSERT INTO mods_categories (joining_mod_id, joining_category_id, is_additional)\n VALUES ($1, $2, FALSE)\n " }, - "7f71e45fc770f423b851a9a89e32a4ba1d395b7ad250a32ab6576d448d34a0a3": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "user_id", - "ordinal": 1, - "type_info": "Int8" - }, - { - "name": "role", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "permissions", - "ordinal": 3, - "type_info": "Int8" - }, - { - "name": "accepted", - "ordinal": 4, - "type_info": "Bool" - }, - { - "name": "payouts_split", - "ordinal": 5, - "type_info": "Numeric" - }, - { - "name": "ordering", - "ordinal": 6, - "type_info": "Int8" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ], - "parameters": { - "Left": [ - "Int8", - "Int8" - ] - } - }, - "query": "\n SELECT id, user_id, role, permissions, accepted, payouts_split, ordering\n FROM team_members\n WHERE (team_id = $1 AND user_id = $2 AND accepted = TRUE)\n " - }, - "7fefc62b251f7a912fa92a1056a831f9b8ade8aa6a76f344756f85efb23bcdfb": { - "describe": { - "columns": [ - { - "name": "project_type", - "ordinal": 0, - "type_info": "Int4" - }, - { - "name": "title", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "description", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "downloads", - "ordinal": 3, - "type_info": "Int4" - }, - { - "name": "follows", - "ordinal": 4, - "type_info": "Int4" - }, - { - "name": "icon_url", - "ordinal": 5, - "type_info": "Varchar" - }, - { - "name": "body", - "ordinal": 6, - "type_info": "Varchar" - }, - { - "name": "published", - "ordinal": 7, - "type_info": "Timestamptz" - }, - { - "name": "updated", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "approved", - "ordinal": 9, - "type_info": "Timestamptz" - }, - { - "name": "status", - "ordinal": 10, - "type_info": "Varchar" - }, - { - "name": "requested_status", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "issues_url", - "ordinal": 12, - "type_info": "Varchar" - }, - { - "name": "source_url", - "ordinal": 13, - "type_info": "Varchar" - }, - { - "name": "wiki_url", - "ordinal": 14, - "type_info": "Varchar" - }, - { - "name": "discord_url", - "ordinal": 15, - "type_info": "Varchar" - }, - { - "name": "license_url", - "ordinal": 16, - "type_info": "Varchar" - }, - { - "name": "team_id", - "ordinal": 17, - "type_info": "Int8" - }, - { - "name": "client_side", - "ordinal": 18, - "type_info": "Int4" - }, - { - "name": "server_side", - "ordinal": 19, - "type_info": "Int4" - }, - { - "name": "license", - "ordinal": 20, - "type_info": "Varchar" - }, - { - "name": "slug", - "ordinal": 21, - "type_info": "Varchar" - }, - { - "name": "moderation_message", - "ordinal": 22, - "type_info": "Varchar" - }, - { - "name": "moderation_message_body", - "ordinal": 23, - "type_info": "Varchar" - }, - { - "name": "flame_anvil_project", - "ordinal": 24, - "type_info": "Int4" - }, - { - "name": "flame_anvil_user", - "ordinal": 25, - "type_info": "Int8" - }, - { - "name": "webhook_sent", - "ordinal": 26, - "type_info": "Bool" - }, - { - "name": "color", - "ordinal": 27, - "type_info": "Int4" - }, - { - "name": "loaders", - "ordinal": 28, - "type_info": "VarcharArray" - }, - { - "name": "game_versions", - "ordinal": 29, - "type_info": "VarcharArray" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - false, - false, - false, - false, - true, - true, - true, - true, - true, - false, - true, - false, - false - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT project_type, title, description, downloads, follows,\n icon_url, body, published,\n updated, approved, status, requested_status,\n issues_url, source_url, wiki_url, discord_url, license_url,\n team_id, client_side, server_side, license, slug,\n moderation_message, moderation_message_body, flame_anvil_project,\n flame_anvil_user, webhook_sent, color, loaders, game_versions\n FROM mods\n WHERE id = $1\n " - }, "8129255d25bf0624d83f50558b668ed7b7f9c264e380d276522fc82bc871939b": { "describe": { "columns": [], @@ -5302,6 +4315,261 @@ }, "query": "\n SELECT loader_id id FROM loaders_versions\n WHERE version_id = $1\n " }, + "ad9c63f994f1e075fea75cff02b14b5a17f3a09ed0132ed8a38d1d22ff7ae5ac": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "project_type", + "ordinal": 1, + "type_info": "Int4" + }, + { + "name": "title", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "description", + "ordinal": 3, + "type_info": "Varchar" + }, + { + "name": "downloads", + "ordinal": 4, + "type_info": "Int4" + }, + { + "name": "follows", + "ordinal": 5, + "type_info": "Int4" + }, + { + "name": "icon_url", + "ordinal": 6, + "type_info": "Varchar" + }, + { + "name": "body", + "ordinal": 7, + "type_info": "Varchar" + }, + { + "name": "published", + "ordinal": 8, + "type_info": "Timestamptz" + }, + { + "name": "updated", + "ordinal": 9, + "type_info": "Timestamptz" + }, + { + "name": "approved", + "ordinal": 10, + "type_info": "Timestamptz" + }, + { + "name": "queued", + "ordinal": 11, + "type_info": "Timestamptz" + }, + { + "name": "status", + "ordinal": 12, + "type_info": "Varchar" + }, + { + "name": "requested_status", + "ordinal": 13, + "type_info": "Varchar" + }, + { + "name": "issues_url", + "ordinal": 14, + "type_info": "Varchar" + }, + { + "name": "source_url", + "ordinal": 15, + "type_info": "Varchar" + }, + { + "name": "wiki_url", + "ordinal": 16, + "type_info": "Varchar" + }, + { + "name": "discord_url", + "ordinal": 17, + "type_info": "Varchar" + }, + { + "name": "license_url", + "ordinal": 18, + "type_info": "Varchar" + }, + { + "name": "team_id", + "ordinal": 19, + "type_info": "Int8" + }, + { + "name": "client_side", + "ordinal": 20, + "type_info": "Int4" + }, + { + "name": "server_side", + "ordinal": 21, + "type_info": "Int4" + }, + { + "name": "license", + "ordinal": 22, + "type_info": "Varchar" + }, + { + "name": "slug", + "ordinal": 23, + "type_info": "Varchar" + }, + { + "name": "moderation_message", + "ordinal": 24, + "type_info": "Varchar" + }, + { + "name": "moderation_message_body", + "ordinal": 25, + "type_info": "Varchar" + }, + { + "name": "client_side_type", + "ordinal": 26, + "type_info": "Varchar" + }, + { + "name": "server_side_type", + "ordinal": 27, + "type_info": "Varchar" + }, + { + "name": "project_type_name", + "ordinal": 28, + "type_info": "Varchar" + }, + { + "name": "flame_anvil_project", + "ordinal": 29, + "type_info": "Int4" + }, + { + "name": "flame_anvil_user", + "ordinal": 30, + "type_info": "Int8" + }, + { + "name": "webhook_sent", + "ordinal": 31, + "type_info": "Bool" + }, + { + "name": "color", + "ordinal": 32, + "type_info": "Int4" + }, + { + "name": "loaders", + "ordinal": 33, + "type_info": "VarcharArray" + }, + { + "name": "game_versions", + "ordinal": 34, + "type_info": "VarcharArray" + }, + { + "name": "categories", + "ordinal": 35, + "type_info": "VarcharArray" + }, + { + "name": "additional_categories", + "ordinal": 36, + "type_info": "VarcharArray" + }, + { + "name": "versions", + "ordinal": 37, + "type_info": "Jsonb" + }, + { + "name": "gallery", + "ordinal": 38, + "type_info": "Jsonb" + }, + { + "name": "donations", + "ordinal": 39, + "type_info": "Jsonb" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + true, + true, + false, + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + true, + true, + true, + false, + false, + false, + true, + true, + false, + true, + false, + false, + null, + null, + null, + null, + null + ], + "parameters": { + "Left": [ + "Int8Array", + "TextArray" + ] + } + }, + "query": "\n SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.body body, m.published published,\n m.updated updated, m.approved approved, m.queued, m.status status, m.requested_status requested_status,\n m.issues_url issues_url, m.source_url source_url, m.wiki_url wiki_url, m.discord_url discord_url, m.license_url license_url,\n m.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body,\n cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user, m.webhook_sent, m.color,\n m.loaders loaders, m.game_versions game_versions,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,\n ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,\n JSONB_AGG(DISTINCT jsonb_build_object('id', v.id, 'date_published', v.date_published)) filter (where v.id is not null) versions,\n JSONB_AGG(DISTINCT jsonb_build_object('image_url', mg.image_url, 'featured', mg.featured, 'title', mg.title, 'description', mg.description, 'created', mg.created, 'ordering', mg.ordering)) filter (where mg.image_url is not null) gallery,\n JSONB_AGG(DISTINCT jsonb_build_object('platform_id', md.joining_platform_id, 'platform_short', dp.short, 'platform_name', dp.name,'url', md.url)) filter (where md.joining_platform_id is not null) donations\n FROM mods m\n INNER JOIN project_types pt ON pt.id = m.project_type\n INNER JOIN side_types cs ON m.client_side = cs.id\n INNER JOIN side_types ss ON m.server_side = ss.id\n LEFT JOIN mods_donations md ON md.joining_mod_id = m.id\n LEFT JOIN donation_platforms dp ON md.joining_platform_id = dp.id\n LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id\n LEFT JOIN categories c ON mc.joining_category_id = c.id\n LEFT JOIN versions v ON v.mod_id = m.id AND v.status = ANY($2)\n LEFT JOIN mods_gallery mg ON mg.mod_id = m.id\n WHERE m.id = ANY($1)\n GROUP BY pt.id, cs.id, ss.id, m.id;\n " + }, "adbe17a5ad3cea333b30b5d6111aff713a8f7dc79ded21f5ba942c4f1108aa8f": { "describe": { "columns": [ @@ -5363,146 +4631,6 @@ }, "query": "\n UPDATE users\n SET balance = balance - $1\n WHERE id = $2\n " }, - "b222ddacd6f94dbd837118a51749b3d720a7f3a414d1cdcf0c26dd4989654b15": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "member_role", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "permissions", - "ordinal": 2, - "type_info": "Int8" - }, - { - "name": "accepted", - "ordinal": 3, - "type_info": "Bool" - }, - { - "name": "payouts_split", - "ordinal": 4, - "type_info": "Numeric" - }, - { - "name": "ordering", - "ordinal": 5, - "type_info": "Int8" - }, - { - "name": "user_id", - "ordinal": 6, - "type_info": "Int8" - }, - { - "name": "github_id", - "ordinal": 7, - "type_info": "Int8" - }, - { - "name": "user_name", - "ordinal": 8, - "type_info": "Varchar" - }, - { - "name": "email", - "ordinal": 9, - "type_info": "Varchar" - }, - { - "name": "avatar_url", - "ordinal": 10, - "type_info": "Varchar" - }, - { - "name": "username", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "bio", - "ordinal": 12, - "type_info": "Varchar" - }, - { - "name": "created", - "ordinal": 13, - "type_info": "Timestamptz" - }, - { - "name": "user_role", - "ordinal": 14, - "type_info": "Varchar" - }, - { - "name": "badges", - "ordinal": 15, - "type_info": "Int8" - }, - { - "name": "balance", - "ordinal": 16, - "type_info": "Numeric" - }, - { - "name": "payout_wallet", - "ordinal": 17, - "type_info": "Varchar" - }, - { - "name": "payout_wallet_type", - "ordinal": 18, - "type_info": "Varchar" - }, - { - "name": "payout_address", - "ordinal": 19, - "type_info": "Varchar" - }, - { - "name": "flame_anvil_key", - "ordinal": 20, - "type_info": "Varchar" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - true, - true, - true, - false, - true, - false, - false, - false, - false, - true, - true, - true, - true - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT tm.id id, tm.role member_role, tm.permissions permissions, tm.accepted accepted, tm.payouts_split payouts_split, tm.ordering ordering,\n u.id user_id, u.github_id github_id, u.name user_name, u.email email,\n u.avatar_url avatar_url, u.username username, u.bio bio,\n u.created created, u.role user_role, u.badges badges, u.balance balance,\n u.payout_wallet payout_wallet, u.payout_wallet_type payout_wallet_type,\n u.payout_address payout_address, u.flame_anvil_key flame_anvil_key\n FROM team_members tm\n INNER JOIN users u ON u.id = tm.user_id\n WHERE tm.team_id = $1\n ORDER BY tm.ordering\n " - }, "b69a6f42965b3e7103fcbf46e39528466926789ff31e9ed2591bb175527ec169": { "describe": { "columns": [], @@ -5528,18 +4656,6 @@ }, "query": "\n UPDATE versions\n SET author_id = $1\n WHERE (author_id = $2)\n " }, - "b8091122d243912e628b06e244bb8ac47cd36903185a50d15ad2368b257ab0e2": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n DELETE FROM notifications\n WHERE id = $1\n " - }, "b903ac4e686ef85ba28d698c668da07860e7f276b261d8f2cebb74e73b094970": { "describe": { "columns": [], @@ -5740,18 +4856,6 @@ }, "query": "\n DELETE FROM dependencies WHERE dependent_id = $1\n " }, - "bdde6a7e476933c109c5b0d7236e033ccb7bf242266f77815a387a370365a10e": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n DELETE FROM notifications_actions\n WHERE notification_id = $1\n " - }, "bec1612d4929d143bc5d6860a57cc036c5ab23e69d750ca5791c620297953c50": { "describe": { "columns": [ @@ -7033,68 +6137,6 @@ }, "query": "\n SELECT f.id id FROM files f\n WHERE f.version_id = $1\n " }, - "e3207ea52c956ca794601e9a4c31aeeedd71c9b961cd6b92c42c14911ae3f74e": { - "describe": { - "columns": [ - { - "name": "user_id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "title", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "text", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "link", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "created", - "ordinal": 4, - "type_info": "Timestamptz" - }, - { - "name": "read", - "ordinal": 5, - "type_info": "Bool" - }, - { - "name": "notification_type", - "ordinal": 6, - "type_info": "Varchar" - }, - { - "name": "actions", - "ordinal": 7, - "type_info": "Jsonb" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - true, - null - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type,\n JSONB_AGG(DISTINCT jsonb_build_object('id', na.id, 'notification_id', na.notification_id, 'title', na.title, 'action_route_method', na.action_route_method, 'action_route', na.action_route)) filter (where na.id is not null) actions\n FROM notifications n\n LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id\n WHERE n.id = $1\n GROUP BY n.id, n.user_id;\n " - }, "e3235e872f98eb85d3eb4a2518fb9dc88049ce62362bfd02623e9b49ac2e9fed": { "describe": { "columns": [ @@ -7184,104 +6226,6 @@ }, "query": "\n UPDATE versions\n SET featured = $1\n WHERE (id = $2)\n " }, - "e5a485770edb23ed77c56cb0bbd7ed28f8789c740c194ff23c44eafab78d440c": { - "describe": { - "columns": [ - { - "name": "github_id", - "ordinal": 0, - "type_info": "Int8" - }, - { - "name": "name", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "email", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "avatar_url", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "username", - "ordinal": 4, - "type_info": "Varchar" - }, - { - "name": "bio", - "ordinal": 5, - "type_info": "Varchar" - }, - { - "name": "created", - "ordinal": 6, - "type_info": "Timestamptz" - }, - { - "name": "role", - "ordinal": 7, - "type_info": "Varchar" - }, - { - "name": "badges", - "ordinal": 8, - "type_info": "Int8" - }, - { - "name": "balance", - "ordinal": 9, - "type_info": "Numeric" - }, - { - "name": "payout_wallet", - "ordinal": 10, - "type_info": "Varchar" - }, - { - "name": "payout_wallet_type", - "ordinal": 11, - "type_info": "Varchar" - }, - { - "name": "payout_address", - "ordinal": 12, - "type_info": "Varchar" - }, - { - "name": "flame_anvil_key", - "ordinal": 13, - "type_info": "Varchar" - } - ], - "nullable": [ - true, - true, - true, - true, - false, - true, - false, - false, - false, - false, - true, - true, - true, - true - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT u.github_id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created, u.role, u.badges,\n u.balance, u.payout_wallet, u.payout_wallet_type,\n u.payout_address, u.flame_anvil_key\n FROM users u\n WHERE u.id = $1\n " - }, "e5bbcf58b8f4abb91757a7dea8d7151cbeaa79fe3aee6542476c1174e82fbe92": { "describe": { "columns": [ diff --git a/src/database/models/notification_item.rs b/src/database/models/notification_item.rs index a25a99894..9250fbe00 100644 --- a/src/database/models/notification_item.rs +++ b/src/database/models/notification_item.rs @@ -121,45 +121,15 @@ impl Notification { executor: E, ) -> Result, sqlx::error::Error> where - E: sqlx::Executor<'a, Database = sqlx::Postgres>, + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let result = sqlx::query!( - " - SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type, - JSONB_AGG(DISTINCT jsonb_build_object('id', na.id, 'notification_id', na.notification_id, 'title', na.title, 'action_route_method', na.action_route_method, 'action_route', na.action_route)) filter (where na.id is not null) actions - FROM notifications n - LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id - WHERE n.id = $1 - GROUP BY n.id, n.user_id; - ", - id as NotificationId, - ) - .fetch_optional(executor) - .await?; - - if let Some(row) = result { - Ok(Some(Notification { - id, - user_id: UserId(row.user_id), - notification_type: row.notification_type, - title: row.title, - text: row.text, - link: row.link, - read: row.read, - created: row.created, - actions: serde_json::from_value( - row.actions.unwrap_or_default(), - ) - .ok() - .unwrap_or_default(), - })) - } else { - Ok(None) - } + Self::get_many(&[id], executor) + .await + .map(|x| x.into_iter().next()) } pub async fn get_many<'a, E>( - notification_ids: Vec, + notification_ids: &[NotificationId], exec: E, ) -> Result, sqlx::Error> where @@ -168,7 +138,7 @@ impl Notification { use futures::stream::TryStreamExt; let notification_ids_parsed: Vec = - notification_ids.into_iter().map(|x| x.0).collect(); + notification_ids.iter().map(|x| x.0).collect(); sqlx::query!( " SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type, @@ -257,35 +227,15 @@ impl Notification { id: NotificationId, transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result, sqlx::error::Error> { - sqlx::query!( - " - DELETE FROM notifications_actions - WHERE notification_id = $1 - ", - id as NotificationId, - ) - .execute(&mut *transaction) - .await?; - - sqlx::query!( - " - DELETE FROM notifications - WHERE id = $1 - ", - id as NotificationId, - ) - .execute(&mut *transaction) - .await?; - - Ok(Some(())) + Self::remove_many(&[id], transaction).await } pub async fn remove_many( - notification_ids: Vec, + notification_ids: &[NotificationId], transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result, sqlx::error::Error> { let notification_ids_parsed: Vec = - notification_ids.into_iter().map(|x| x.0).collect(); + notification_ids.iter().map(|x| x.0).collect(); sqlx::query!( " diff --git a/src/database/models/project_item.rs b/src/database/models/project_item.rs index 8814f951a..4ae287020 100644 --- a/src/database/models/project_item.rs +++ b/src/database/models/project_item.rs @@ -119,6 +119,11 @@ impl ProjectBuilder { published: Utc::now(), updated: Utc::now(), approved: None, + queued: if self.status == ProjectStatus::Processing { + Some(Utc::now()) + } else { + None + }, status: self.status, requested_status: self.requested_status, downloads: 0, @@ -202,6 +207,7 @@ pub struct Project { pub published: DateTime, pub updated: DateTime, pub approved: Option>, + pub queued: Option>, pub status: ProjectStatus, pub requested_status: Option, pub downloads: i32, @@ -281,69 +287,15 @@ impl Project { executor: E, ) -> Result, sqlx::error::Error> where - E: sqlx::Executor<'a, Database = sqlx::Postgres>, + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let result = sqlx::query!( - " - SELECT project_type, title, description, downloads, follows, - icon_url, body, published, - updated, approved, status, requested_status, - issues_url, source_url, wiki_url, discord_url, license_url, - team_id, client_side, server_side, license, slug, - moderation_message, moderation_message_body, flame_anvil_project, - flame_anvil_user, webhook_sent, color, loaders, game_versions - FROM mods - WHERE id = $1 - ", - id as ProjectId, - ) - .fetch_optional(executor) - .await?; - - if let Some(row) = result { - Ok(Some(Project { - id, - project_type: ProjectTypeId(row.project_type), - team_id: TeamId(row.team_id), - title: row.title, - description: row.description, - downloads: row.downloads, - body_url: None, - icon_url: row.icon_url, - published: row.published, - updated: row.updated, - issues_url: row.issues_url, - source_url: row.source_url, - wiki_url: row.wiki_url, - license_url: row.license_url, - discord_url: row.discord_url, - client_side: SideTypeId(row.client_side), - status: ProjectStatus::from_str(&row.status), - requested_status: row - .requested_status - .map(|x| ProjectStatus::from_str(&x)), - server_side: SideTypeId(row.server_side), - license: row.license, - slug: row.slug, - body: row.body, - follows: row.follows, - moderation_message: row.moderation_message, - moderation_message_body: row.moderation_message_body, - approved: row.approved, - flame_anvil_project: row.flame_anvil_project, - flame_anvil_user: row.flame_anvil_user.map(UserId), - webhook_sent: row.webhook_sent, - color: row.color.map(|x| x as u32), - loaders: row.loaders, - game_versions: row.game_versions, - })) - } else { - Ok(None) - } + Project::get_many(&[id], executor) + .await + .map(|x| x.into_iter().next()) } pub async fn get_many<'a, E>( - project_ids: Vec, + project_ids: &[ProjectId], exec: E, ) -> Result, sqlx::Error> where @@ -352,12 +304,12 @@ impl Project { use futures::stream::TryStreamExt; let project_ids_parsed: Vec = - project_ids.into_iter().map(|x| x.0).collect(); + project_ids.iter().map(|x| x.0).collect(); let projects = sqlx::query!( " SELECT id, project_type, title, description, downloads, follows, icon_url, body, published, - updated, approved, status, requested_status, + updated, approved, queued, status, requested_status, issues_url, source_url, wiki_url, discord_url, license_url, team_id, client_side, server_side, license, slug, moderation_message, moderation_message_body, flame_anvil_project, @@ -406,6 +358,7 @@ impl Project { color: m.color.map(|x| x as u32), loaders: m.loaders, game_versions: m.game_versions, + queued: m.queued, })) }) .try_collect::>() @@ -677,125 +630,9 @@ impl Project { where E: sqlx::Executor<'a, Database = sqlx::Postgres>, { - let result = sqlx::query!( - " - SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows, - m.icon_url icon_url, m.body body, m.published published, - m.updated updated, m.approved approved, m.status status, m.requested_status requested_status, - m.issues_url issues_url, m.source_url source_url, m.wiki_url wiki_url, m.discord_url discord_url, m.license_url license_url, - m.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body, - cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user, m.webhook_sent webhook_sent, m.color, - m.loaders loaders, m.game_versions game_versions, - ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories, - ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories, - JSONB_AGG(DISTINCT jsonb_build_object('id', v.id, 'date_published', v.date_published)) filter (where v.id is not null) versions, - JSONB_AGG(DISTINCT jsonb_build_object('image_url', mg.image_url, 'featured', mg.featured, 'title', mg.title, 'description', mg.description, 'created', mg.created, 'ordering', mg.ordering)) filter (where mg.image_url is not null) gallery, - JSONB_AGG(DISTINCT jsonb_build_object('platform_id', md.joining_platform_id, 'platform_short', dp.short, 'platform_name', dp.name,'url', md.url)) filter (where md.joining_platform_id is not null) donations - FROM mods m - INNER JOIN project_types pt ON pt.id = m.project_type - INNER JOIN side_types cs ON m.client_side = cs.id - INNER JOIN side_types ss ON m.server_side = ss.id - LEFT JOIN mods_donations md ON md.joining_mod_id = m.id - LEFT JOIN donation_platforms dp ON md.joining_platform_id = dp.id - LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id - LEFT JOIN categories c ON mc.joining_category_id = c.id - LEFT JOIN versions v ON v.mod_id = m.id AND v.status = ANY($2) - LEFT JOIN mods_gallery mg ON mg.mod_id = m.id - WHERE m.id = $1 - GROUP BY pt.id, cs.id, ss.id, m.id; - ", - id as ProjectId, - &*crate::models::projects::VersionStatus::iterator().filter(|x| x.is_listed()).map(|x| x.to_string()).collect::>() - ) - .fetch_optional(executor) - .await?; - - if let Some(m) = result { - Ok(Some(QueryProject { - inner: Project { - id: ProjectId(m.id), - project_type: ProjectTypeId(m.project_type), - team_id: TeamId(m.team_id), - title: m.title.clone(), - description: m.description.clone(), - downloads: m.downloads, - body_url: None, - icon_url: m.icon_url.clone(), - published: m.published, - updated: m.updated, - issues_url: m.issues_url.clone(), - source_url: m.source_url.clone(), - wiki_url: m.wiki_url.clone(), - license_url: m.license_url.clone(), - discord_url: m.discord_url.clone(), - client_side: SideTypeId(m.client_side), - status: ProjectStatus::from_str(&m.status), - requested_status: m - .requested_status - .map(|x| ProjectStatus::from_str(&x)), - server_side: SideTypeId(m.server_side), - license: m.license.clone(), - slug: m.slug.clone(), - body: m.body.clone(), - follows: m.follows, - moderation_message: m.moderation_message, - moderation_message_body: m.moderation_message_body, - approved: m.approved, - flame_anvil_project: m.flame_anvil_project, - flame_anvil_user: m.flame_anvil_user.map(UserId), - webhook_sent: m.webhook_sent, - color: m.color.map(|x| x as u32), - loaders: m.loaders, - game_versions: m.game_versions, - }, - project_type: m.project_type_name, - categories: m.categories.unwrap_or_default(), - additional_categories: m - .additional_categories - .unwrap_or_default(), - versions: { - #[derive(Deserialize)] - struct Version { - pub id: VersionId, - pub date_published: DateTime, - } - - let mut versions: Vec = - serde_json::from_value(m.versions.unwrap_or_default()) - .ok() - .unwrap_or_default(); - - versions.sort_by(|a, b| { - a.date_published.cmp(&b.date_published) - }); - - versions.into_iter().map(|x| x.id).collect() - }, - gallery_items: { - let mut gallery: Vec = - serde_json::from_value(m.gallery.unwrap_or_default()) - .ok() - .unwrap_or_default(); - - gallery.sort_by(|a, b| a.ordering.cmp(&b.ordering)); - - gallery - }, - donation_urls: serde_json::from_value( - m.donations.unwrap_or_default(), - ) - .ok() - .unwrap_or_default(), - client_side: crate::models::projects::SideType::from_str( - &m.client_side_type, - ), - server_side: crate::models::projects::SideType::from_str( - &m.server_side_type, - ), - })) - } else { - Ok(None) - } + Project::get_many_full(&[id], executor) + .await + .map(|x| x.into_iter().next()) } pub async fn get_many_full<'a, E>( @@ -813,7 +650,7 @@ impl Project { " SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows, m.icon_url icon_url, m.body body, m.published published, - m.updated updated, m.approved approved, m.status status, m.requested_status requested_status, + m.updated updated, m.approved approved, m.queued, m.status status, m.requested_status requested_status, m.issues_url issues_url, m.source_url source_url, m.wiki_url wiki_url, m.discord_url discord_url, m.license_url license_url, m.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body, cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user, m.webhook_sent, m.color, @@ -882,6 +719,7 @@ impl Project { color: m.color.map(|x| x as u32), loaders: m.loaders, game_versions: m.game_versions, + queued: m.queued, }, project_type: m.project_type_name, categories: m.categories.unwrap_or_default(), diff --git a/src/database/models/report_item.rs b/src/database/models/report_item.rs index b480e0a92..6e2faa24d 100644 --- a/src/database/models/report_item.rs +++ b/src/database/models/report_item.rs @@ -60,36 +60,13 @@ impl Report { where E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let result = sqlx::query!( - " - SELECT rt.name, r.mod_id, r.version_id, r.user_id, r.body, r.reporter, r.created - FROM reports r - INNER JOIN report_types rt ON rt.id = r.report_type_id - WHERE r.id = $1 - ", - id as ReportId, - ) - .fetch_optional(exec) - .await?; - - if let Some(row) = result { - Ok(Some(QueryReport { - id, - report_type: row.name, - project_id: row.mod_id.map(ProjectId), - version_id: row.version_id.map(VersionId), - user_id: row.user_id.map(UserId), - body: row.body, - reporter: UserId(row.reporter), - created: row.created, - })) - } else { - Ok(None) - } + Self::get_many(&[id], exec) + .await + .map(|x| x.into_iter().next()) } pub async fn get_many<'a, E>( - report_ids: Vec, + report_ids: &[ReportId], exec: E, ) -> Result, sqlx::Error> where @@ -98,7 +75,7 @@ impl Report { use futures::stream::TryStreamExt; let report_ids_parsed: Vec = - report_ids.into_iter().map(|x| x.0).collect(); + report_ids.iter().map(|x| x.0).collect(); let reports = sqlx::query!( " SELECT r.id, rt.name, r.mod_id, r.version_id, r.user_id, r.body, r.reporter, r.created diff --git a/src/database/models/team_item.rs b/src/database/models/team_item.rs index 06f5006ac..f09f3e57e 100644 --- a/src/database/models/team_item.rs +++ b/src/database/models/team_item.rs @@ -157,71 +157,13 @@ impl TeamMember { executor: E, ) -> Result, super::DatabaseError> where - E: sqlx::Executor<'a, Database = sqlx::Postgres>, + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - use futures::stream::TryStreamExt; - - let team_members = sqlx::query!( - " - SELECT tm.id id, tm.role member_role, tm.permissions permissions, tm.accepted accepted, tm.payouts_split payouts_split, tm.ordering ordering, - u.id user_id, u.github_id github_id, u.name user_name, u.email email, - u.avatar_url avatar_url, u.username username, u.bio bio, - u.created created, u.role user_role, u.badges badges, u.balance balance, - u.payout_wallet payout_wallet, u.payout_wallet_type payout_wallet_type, - u.payout_address payout_address, u.flame_anvil_key flame_anvil_key - FROM team_members tm - INNER JOIN users u ON u.id = tm.user_id - WHERE tm.team_id = $1 - ORDER BY tm.ordering - ", - id as TeamId, - ) - .fetch_many(executor) - .try_filter_map(|e| async { - if let Some(m) = e.right() { - - Ok(Some(Ok(QueryTeamMember { - id: TeamMemberId(m.id), - team_id: id, - role: m.member_role, - permissions: Permissions::from_bits(m.permissions as u64).unwrap_or_default(), - accepted: m.accepted, - user: User { - id: UserId(m.user_id), - github_id: m.github_id, - name: m.user_name, - email: m.email, - avatar_url: m.avatar_url, - username: m.username, - bio: m.bio, - created: m.created, - role: m.user_role, - badges: Badges::from_bits(m.badges as u64).unwrap_or_default(), - balance: m.balance, - payout_wallet: m.payout_wallet.map(|x| RecipientWallet::from_string(&x)), - payout_wallet_type: m.payout_wallet_type.map(|x| RecipientType::from_string(&x)), - payout_address: m.payout_address, - flame_anvil_key: m.flame_anvil_key, - }, - payouts_split: m.payouts_split, - ordering: m.ordering, - }))) - } else { - Ok(None) - } - }) - .try_collect::>>() - .await?; - - let team_members = team_members - .into_iter() - .collect::, super::DatabaseError>>()?; - - Ok(team_members) + Self::get_from_team_full_many(&[id], executor).await } pub async fn get_from_team_full_many<'a, E>( - team_ids: Vec, + team_ids: &[TeamId], exec: E, ) -> Result, super::DatabaseError> where @@ -229,8 +171,7 @@ impl TeamMember { { use futures::stream::TryStreamExt; - let team_ids_parsed: Vec = - team_ids.into_iter().map(|x| x.0).collect(); + let team_ids_parsed: Vec = team_ids.iter().map(|x| x.0).collect(); let teams = sqlx::query!( " @@ -394,38 +335,14 @@ impl TeamMember { where E: sqlx::Executor<'a, Database = sqlx::Postgres>, { - let result = sqlx::query!( - " - SELECT id, user_id, role, permissions, accepted, payouts_split, ordering - FROM team_members - WHERE (team_id = $1 AND user_id = $2 AND accepted = TRUE) - ", - id as TeamId, - user_id as UserId - ) - .fetch_optional(executor) - .await?; - - if let Some(m) = result { - Ok(Some(TeamMember { - id: TeamMemberId(m.id), - team_id: id, - user_id, - role: m.role, - permissions: Permissions::from_bits(m.permissions as u64) - .unwrap_or_default(), - accepted: m.accepted, - payouts_split: m.payouts_split, - ordering: m.ordering, - })) - } else { - Ok(None) - } + Self::get_from_user_id_many(&[id], user_id, executor) + .await + .map(|x| x.into_iter().next()) } /// Gets team members from user ids and team ids. Does not return pending members. pub async fn get_from_user_id_many<'a, 'b, E>( - team_ids: Vec, + team_ids: &[TeamId], user_id: UserId, executor: E, ) -> Result, super::DatabaseError> @@ -434,8 +351,7 @@ impl TeamMember { { use futures::stream::TryStreamExt; - let team_ids_parsed: Vec = - team_ids.into_iter().map(|x| x.0).collect(); + let team_ids_parsed: Vec = team_ids.iter().map(|x| x.0).collect(); let team_members = sqlx::query!( " diff --git a/src/database/models/user_item.rs b/src/database/models/user_item.rs index 2bc06f98f..14837a50e 100644 --- a/src/database/models/user_item.rs +++ b/src/database/models/user_item.rs @@ -56,49 +56,11 @@ impl User { executor: E, ) -> Result, sqlx::error::Error> where - E: sqlx::Executor<'a, Database = sqlx::Postgres>, + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let result = sqlx::query!( - " - SELECT u.github_id, u.name, u.email, - u.avatar_url, u.username, u.bio, - u.created, u.role, u.badges, - u.balance, u.payout_wallet, u.payout_wallet_type, - u.payout_address, u.flame_anvil_key - FROM users u - WHERE u.id = $1 - ", - id as UserId, - ) - .fetch_optional(executor) - .await?; - - if let Some(row) = result { - Ok(Some(User { - id, - github_id: row.github_id, - name: row.name, - email: row.email, - avatar_url: row.avatar_url, - username: row.username, - bio: row.bio, - created: row.created, - role: row.role, - badges: Badges::from_bits(row.badges as u64) - .unwrap_or_default(), - balance: row.balance, - payout_wallet: row - .payout_wallet - .map(|x| RecipientWallet::from_string(&x)), - payout_wallet_type: row - .payout_wallet_type - .map(|x| RecipientType::from_string(&x)), - payout_address: row.payout_address, - flame_anvil_key: row.flame_anvil_key, - })) - } else { - Ok(None) - } + Self::get_many(&[id], executor) + .await + .map(|x| x.into_iter().next()) } pub async fn get_from_github_id<'a, 'b, E>( @@ -202,7 +164,7 @@ impl User { } pub async fn get_many<'a, E>( - user_ids: Vec, + user_ids: &[UserId], exec: E, ) -> Result, sqlx::Error> where @@ -210,8 +172,7 @@ impl User { { use futures::stream::TryStreamExt; - let user_ids_parsed: Vec = - user_ids.into_iter().map(|x| x.0).collect(); + let user_ids_parsed: Vec = user_ids.iter().map(|x| x.0).collect(); let users = sqlx::query!( " SELECT u.id, u.github_id, u.name, u.email, diff --git a/src/database/models/version_item.rs b/src/database/models/version_item.rs index fddaab636..824976435 100644 --- a/src/database/models/version_item.rs +++ b/src/database/models/version_item.rs @@ -573,46 +573,15 @@ impl Version { executor: E, ) -> Result, sqlx::error::Error> where - E: sqlx::Executor<'a, Database = sqlx::Postgres>, + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let result = sqlx::query!( - " - SELECT v.mod_id, v.author_id, v.name, v.version_number, - v.changelog, v.date_published, v.downloads, - v.version_type, v.featured, v.status, v.requested_status - FROM versions v - WHERE v.id = $1 - ", - id as VersionId, - ) - .fetch_optional(executor) - .await?; - - if let Some(row) = result { - Ok(Some(Version { - id, - project_id: ProjectId(row.mod_id), - author_id: UserId(row.author_id), - name: row.name, - version_number: row.version_number, - changelog: row.changelog, - changelog_url: None, - date_published: row.date_published, - downloads: row.downloads, - version_type: row.version_type, - featured: row.featured, - status: VersionStatus::from_str(&row.status), - requested_status: row - .requested_status - .map(|x| VersionStatus::from_str(&x)), - })) - } else { - Ok(None) - } + Self::get_many(&[id], executor) + .await + .map(|x| x.into_iter().next()) } pub async fn get_many<'a, E>( - version_ids: Vec, + version_ids: &[VersionId], exec: E, ) -> Result, sqlx::Error> where @@ -621,7 +590,7 @@ impl Version { use futures::stream::TryStreamExt; let version_ids_parsed: Vec = - version_ids.into_iter().map(|x| x.0).collect(); + version_ids.iter().map(|x| x.0).collect(); let versions = sqlx::query!( " SELECT v.id, v.mod_id, v.author_id, v.name, v.version_number, @@ -664,151 +633,15 @@ impl Version { executor: E, ) -> Result, sqlx::error::Error> where - E: sqlx::Executor<'a, Database = sqlx::Postgres>, + E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, { - let result = sqlx::query!( - " - SELECT v.id id, v.mod_id mod_id, v.author_id author_id, v.name version_name, v.version_number version_number, - v.changelog changelog, v.date_published date_published, v.downloads downloads, - v.version_type version_type, v.featured featured, v.status status, v.requested_status requested_status, - JSONB_AGG(DISTINCT jsonb_build_object('version', gv.version, 'created', gv.created)) filter (where gv.version is not null) game_versions, - ARRAY_AGG(DISTINCT l.loader) filter (where l.loader is not null) loaders, - JSONB_AGG(DISTINCT jsonb_build_object('id', f.id, 'url', f.url, 'filename', f.filename, 'primary', f.is_primary, 'size', f.size, 'file_type', f.file_type)) filter (where f.id is not null) files, - JSONB_AGG(DISTINCT jsonb_build_object('algorithm', h.algorithm, 'hash', encode(h.hash, 'escape'), 'file_id', h.file_id)) filter (where h.hash is not null) hashes, - JSONB_AGG(DISTINCT jsonb_build_object('project_id', d.mod_dependency_id, 'version_id', d.dependency_id, 'dependency_type', d.dependency_type,'file_name', dependency_file_name)) filter (where d.dependency_type is not null) dependencies - FROM versions v - LEFT OUTER JOIN game_versions_versions gvv on v.id = gvv.joining_version_id - LEFT OUTER JOIN game_versions gv on gvv.game_version_id = gv.id - LEFT OUTER JOIN loaders_versions lv on v.id = lv.version_id - LEFT OUTER JOIN loaders l on lv.loader_id = l.id - LEFT OUTER JOIN files f on v.id = f.version_id - LEFT OUTER JOIN hashes h on f.id = h.file_id - LEFT OUTER JOIN dependencies d on v.id = d.dependent_id - WHERE v.id = $1 - GROUP BY v.id; - ", - id as VersionId, - ) - .fetch_optional(executor) - .await?; - - if let Some(v) = result { - Ok(Some(QueryVersion { - inner: Version { - id: VersionId(v.id), - project_id: ProjectId(v.mod_id), - author_id: UserId(v.author_id), - name: v.version_name, - version_number: v.version_number, - changelog: v.changelog, - changelog_url: None, - date_published: v.date_published, - downloads: v.downloads, - version_type: v.version_type, - featured: v.featured, - status: VersionStatus::from_str(&v.status), - requested_status: v - .requested_status - .map(|x| VersionStatus::from_str(&x)), - }, - files: { - #[derive(Deserialize, Debug)] - struct Hash { - pub file_id: FileId, - pub algorithm: String, - pub hash: String, - } - - #[derive(Deserialize, Debug)] - struct File { - pub id: FileId, - pub url: String, - pub filename: String, - pub primary: bool, - pub size: u32, - pub file_type: Option, - } - - let hashes: Vec = - serde_json::from_value(v.hashes.unwrap_or_default()) - .ok() - .unwrap_or_default(); - - let files: Vec = - serde_json::from_value(v.files.unwrap_or_default()) - .ok() - .unwrap_or_default(); - - let mut files = files - .into_iter() - .map(|x| { - let mut file_hashes = HashMap::new(); - - for hash in &hashes { - if hash.file_id == x.id { - file_hashes.insert( - hash.algorithm.clone(), - hash.hash.clone(), - ); - } - } - - QueryFile { - id: x.id, - url: x.url, - filename: x.filename, - hashes: file_hashes, - primary: x.primary, - size: x.size, - file_type: x.file_type, - } - }) - .collect::>(); - - files.sort_by(|a, b| { - if a.primary { - Ordering::Less - } else if b.primary { - Ordering::Greater - } else { - a.filename.cmp(&b.filename) - } - }); - - files - }, - game_versions: { - #[derive(Deserialize)] - struct GameVersion { - pub version: String, - pub created: DateTime, - } - - let mut game_versions: Vec = - serde_json::from_value( - v.game_versions.unwrap_or_default(), - ) - .ok() - .unwrap_or_default(); - - game_versions.sort_by(|a, b| a.created.cmp(&b.created)); - - game_versions.into_iter().map(|x| x.version).collect() - }, - loaders: v.loaders.unwrap_or_default(), - dependencies: serde_json::from_value( - v.dependencies.unwrap_or_default(), - ) - .ok() - .unwrap_or_default(), - })) - } else { - Ok(None) - } + Self::get_many_full(&[id], executor) + .await + .map(|x| x.into_iter().next()) } pub async fn get_many_full<'a, E>( - version_ids: Vec, + version_ids: &[VersionId], exec: E, ) -> Result, sqlx::Error> where @@ -817,7 +650,7 @@ impl Version { use futures::stream::TryStreamExt; let version_ids_parsed: Vec = - version_ids.into_iter().map(|x| x.0).collect(); + version_ids.iter().map(|x| x.0).collect(); sqlx::query!( " SELECT v.id id, v.mod_id mod_id, v.author_id author_id, v.name version_name, v.version_number version_number, diff --git a/src/main.rs b/src/main.rs index 8a36c84d9..8d6ed1713 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use crate::ratelimit::middleware::RateLimiter; use crate::util::env::{parse_strings_from_var, parse_var}; use actix_cors::Cors; use actix_web::{web, App, HttpServer}; +use chrono::{DateTime, Utc}; use env_logger::Env; use log::{error, info, warn}; use search::indexing::index_projects; @@ -201,6 +202,74 @@ async fn main() -> std::io::Result<()> { } }); + // Reminding moderators to review projects which have been in the queue longer than 24hr + let pool_ref = pool.clone(); + let webhook_message_sent = Arc::new(Mutex::new(Vec::<( + database::models::ProjectId, + DateTime, + )>::new())); + + scheduler.run(std::time::Duration::from_secs(10 * 60), move || { + let pool_ref = pool_ref.clone(); + let webhook_message_sent_ref = webhook_message_sent.clone(); + info!("Checking reviewed projects submitted more than 24hrs ago"); + + async move { + let do_steps = async { + use futures::TryStreamExt; + + let project_ids = sqlx::query!( + " + SELECT id FROM mods + WHERE status = $1 AND queued < NOW() - INTERVAL '1 day' + ORDER BY updated ASC + ", + crate::models::projects::ProjectStatus::Processing.as_str(), + ) + .fetch_many(&pool_ref) + .try_filter_map(|e| async { + Ok(e.right().map(|m| database::models::ProjectId(m.id))) + }) + .try_collect::>() + .await?; + + let mut webhook_message_sent_ref = webhook_message_sent_ref.lock().await; + + webhook_message_sent_ref.retain(|x| Utc::now() - x.1 > chrono::Duration::hours(12)); + + for project in project_ids { + if webhook_message_sent_ref.iter().any(|x| x.0 == project) { continue; } + + if let Ok(webhook_url) = + dotenvy::var("MODERATION_DISCORD_WEBHOOK") + { + util::webhook::send_discord_webhook( + project.into(), + &pool_ref, + webhook_url, + Some("<@&783155186491195394> This project has been in the queue for over 24 hours!".to_string()), + ) + .await + .ok(); + + webhook_message_sent_ref.push((project, Utc::now())); + } + } + + Ok::<(), crate::routes::ApiError>(()) + }; + + if let Err(e) = do_steps.await { + warn!( + "Checking reviewed projects submitted more than 24hrs ago failed: {:?}", + e + ); + } + + info!("Finished checking reviewed projects submitted more than 24hrs ago"); + } + }); + scheduler::schedule_versions(&mut scheduler, pool.clone()); let download_queue = Arc::new(DownloadQueue::new()); diff --git a/src/models/projects.rs b/src/models/projects.rs index ce9a953ec..3b7fa6824 100644 --- a/src/models/projects.rs +++ b/src/models/projects.rs @@ -48,6 +48,8 @@ pub struct Project { /// The date at which the project was first approved. //pub approved: Option>, pub approved: Option>, + /// The date at which the project entered the moderation queue + pub queued: Option>, /// The status of the project pub status: ProjectStatus, @@ -122,6 +124,7 @@ impl From for Project { published: m.published, updated: m.updated, approved: m.approved, + queued: m.queued, status: m.status, requested_status: m.requested_status, moderator_message: if let Some(message) = m.moderation_message { diff --git a/src/routes/moderation.rs b/src/routes/moderation.rs index fdc086ef3..09f2588c0 100644 --- a/src/routes/moderation.rs +++ b/src/routes/moderation.rs @@ -30,7 +30,7 @@ pub async fn get_projects( " SELECT id FROM mods WHERE status = $1 - ORDER BY updated ASC + ORDER BY queued ASC LIMIT $2; ", ProjectStatus::Processing.as_str(), diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index 7a4a13892..f79406080 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -32,7 +32,7 @@ pub async fn notifications_get( let notifications_data: Vec = database::models::notification_item::Notification::get_many( - notification_ids, + ¬ification_ids, &**pool, ) .await?; @@ -127,13 +127,13 @@ pub async fn notifications_delete( serde_json::from_str::>(&ids.ids)? .into_iter() .map(|x| x.into()) - .collect(); + .collect::>(); let mut transaction = pool.begin().await?; let notifications_data = database::models::notification_item::Notification::get_many( - notification_ids, + ¬ification_ids, &**pool, ) .await?; @@ -148,7 +148,7 @@ pub async fn notifications_delete( } database::models::notification_item::Notification::remove_many( - notifications, + ¬ifications, &mut transaction, ) .await?; diff --git a/src/routes/project_creation.rs b/src/routes/project_creation.rs index f0e4efb6d..73c0f1e4f 100644 --- a/src/routes/project_creation.rs +++ b/src/routes/project_creation.rs @@ -802,6 +802,7 @@ async fn project_create_inner( published: now, updated: now, approved: None, + queued: None, status, requested_status: project_builder.requested_status, moderator_message: None, @@ -844,6 +845,7 @@ async fn project_create_inner( response.id, pool, webhook_url, + None, ) .await .ok(); diff --git a/src/routes/projects.rs b/src/routes/projects.rs index fb4b0c0ec..93160c729 100644 --- a/src/routes/projects.rs +++ b/src/routes/projects.rs @@ -256,12 +256,13 @@ pub async fn dependency_list( }) .collect::>(); + let dep_version_ids = dependencies + .iter() + .filter_map(|x| x.0) + .collect::>(); let (projects_result, versions_result) = futures::future::try_join( database::Project::get_many_full(&project_ids, &**pool), - database::Version::get_many_full( - dependencies.iter().filter_map(|x| x.0).collect(), - &**pool, - ), + database::Version::get_many_full(&dep_version_ids, &**pool), ) .await?; @@ -502,18 +503,7 @@ pub async fn project_edit( sqlx::query!( " UPDATE mods - SET moderation_message = NULL - WHERE (id = $1) - ", - id as database::models::ids::ProjectId, - ) - .execute(&mut *transaction) - .await?; - - sqlx::query!( - " - UPDATE mods - SET moderation_message_body = NULL + SET moderation_message = NULL, moderation_message_body = NULL, queued = NOW() WHERE (id = $1) ", id as database::models::ids::ProjectId, @@ -528,6 +518,7 @@ pub async fn project_edit( project_item.inner.id.into(), &pool, webhook_url, + None, ) .await .ok(); @@ -557,6 +548,7 @@ pub async fn project_edit( project_item.inner.id.into(), &pool, webhook_url, + None, ) .await .ok(); @@ -1234,9 +1226,12 @@ pub async fn projects_edit( ))); } + let team_ids = projects_data + .iter() + .map(|x| x.inner.team_id) + .collect::>(); let team_members = database::models::TeamMember::get_from_team_full_many( - projects_data.iter().map(|x| x.inner.team_id).collect(), - &**pool, + &team_ids, &**pool, ) .await?; diff --git a/src/routes/reports.rs b/src/routes/reports.rs index cb22f79f1..c866dc487 100644 --- a/src/routes/reports.rs +++ b/src/routes/reports.rs @@ -184,7 +184,8 @@ pub async fn reports( .await?; let query_reports = crate::database::models::report_item::Report::get_many( - report_ids, &**pool, + &report_ids, + &**pool, ) .await?; diff --git a/src/routes/teams.rs b/src/routes/teams.rs index 7b757b691..15fffceb1 100644 --- a/src/routes/teams.rs +++ b/src/routes/teams.rs @@ -121,11 +121,11 @@ pub async fn teams_get( .collect::>(); let teams_data = - TeamMember::get_from_team_full_many(team_ids.clone(), &**pool).await?; + TeamMember::get_from_team_full_many(&team_ids, &**pool).await?; let current_user = get_user_from_headers(req.headers(), &**pool).await.ok(); let accepted = if let Some(user) = current_user { - TeamMember::get_from_user_id_many(team_ids, user.id.into(), &**pool) + TeamMember::get_from_user_id_many(&team_ids, user.id.into(), &**pool) .await? .into_iter() .map(|m| m.team_id.0) diff --git a/src/routes/updates.rs b/src/routes/updates.rs index c3a674e25..585dc5bb1 100644 --- a/src/routes/updates.rs +++ b/src/routes/updates.rs @@ -45,7 +45,7 @@ pub async fn forge_updates( .await?; let versions = - database::models::Version::get_many_full(version_ids, &**pool).await?; + database::models::Version::get_many_full(&version_ids, &**pool).await?; let mut versions = filter_authorized_versions(versions, &user_option, &pool).await?; diff --git a/src/routes/users.rs b/src/routes/users.rs index 1c06e03a0..e7072250d 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -44,9 +44,9 @@ pub async fn users_get( let user_ids = serde_json::from_str::>(&ids.ids)? .into_iter() .map(|x| x.into()) - .collect(); + .collect::>(); - let users_data = User::get_many(user_ids, &**pool).await?; + let users_data = User::get_many(&user_ids, &**pool).await?; let users: Vec = users_data.into_iter().map(From::from).collect(); diff --git a/src/routes/v1/users.rs b/src/routes/v1/users.rs index 2f1a0b1f4..a3389d2aa 100644 --- a/src/routes/v1/users.rs +++ b/src/routes/v1/users.rs @@ -31,7 +31,7 @@ pub async fn mods_list( let project_data = User::get_projects(id, &**pool).await?; let response: Vec<_> = - crate::database::Project::get_many(project_data, &**pool) + crate::database::Project::get_many(&project_data, &**pool) .await? .into_iter() .filter(|x| can_view_private || x.status.is_approved()) diff --git a/src/routes/v1/versions.rs b/src/routes/v1/versions.rs index 4bd07b9dc..7d2a62e55 100644 --- a/src/routes/v1/versions.rs +++ b/src/routes/v1/versions.rs @@ -84,7 +84,7 @@ pub async fn version_list( .await?; let mut versions = - database::models::Version::get_many_full(version_ids, &**pool) + database::models::Version::get_many_full(&version_ids, &**pool) .await?; let mut response = versions @@ -165,9 +165,9 @@ pub async fn versions_get( let version_ids = serde_json::from_str::>(&ids.ids)? .into_iter() .map(|x| x.into()) - .collect(); + .collect::>(); let versions_data = - database::models::Version::get_many_full(version_ids, &**pool).await?; + database::models::Version::get_many_full(&version_ids, &**pool).await?; let mut versions = Vec::new(); diff --git a/src/routes/version_file.rs b/src/routes/version_file.rs index 8019bcdd3..96e94e902 100644 --- a/src/routes/version_file.rs +++ b/src/routes/version_file.rs @@ -63,14 +63,12 @@ pub async fn get_version_from_hash( .fetch_all(&**pool) .await?; - let versions_data = database::models::Version::get_many_full( - result - .iter() - .map(|x| database::models::VersionId(x.version_id)) - .collect(), - &**pool, - ) - .await?; + let version_ids = result + .iter() + .map(|x| database::models::VersionId(x.version_id)) + .collect::>(); + let versions_data = + database::models::Version::get_many_full(&version_ids, &**pool).await?; if let Some(first) = versions_data.first() { if hash_query.multiple { @@ -357,14 +355,12 @@ pub async fn get_versions_from_hashes( .fetch_all(&**pool) .await?; - let versions_data = database::models::Version::get_many_full( - result - .iter() - .map(|x| database::models::VersionId(x.version_id)) - .collect(), - &**pool, - ) - .await?; + let version_ids = result + .iter() + .map(|x| database::models::VersionId(x.version_id)) + .collect::>(); + let versions_data = + database::models::Version::get_many_full(&version_ids, &**pool).await?; let response: Result, ApiError> = result .into_iter() @@ -518,11 +514,10 @@ pub async fn update_files( } } - let versions = database::models::Version::get_many_full( - version_ids.keys().copied().collect(), - &**pool, - ) - .await?; + let query_version_ids = version_ids.keys().copied().collect::>(); + let versions = + database::models::Version::get_many_full(&query_version_ids, &**pool) + .await?; let mut response = HashMap::new(); diff --git a/src/routes/versions.rs b/src/routes/versions.rs index 320d141b5..9d0b32f5d 100644 --- a/src/routes/versions.rs +++ b/src/routes/versions.rs @@ -67,7 +67,7 @@ pub async fn version_list( .await?; let mut versions = - database::models::Version::get_many_full(version_ids, &**pool) + database::models::Version::get_many_full(&version_ids, &**pool) .await?; let mut response = versions @@ -179,9 +179,9 @@ pub async fn versions_get( serde_json::from_str::>(&ids.ids)? .into_iter() .map(|x| x.into()) - .collect(); + .collect::>(); let versions_data = - database::models::Version::get_many_full(version_ids, &**pool).await?; + database::models::Version::get_many_full(&version_ids, &**pool).await?; let user_option = get_user_from_headers(req.headers(), &**pool).await.ok(); diff --git a/src/util/webhook.rs b/src/util/webhook.rs index 3e442c95d..f020e80dc 100644 --- a/src/util/webhook.rs +++ b/src/util/webhook.rs @@ -55,6 +55,7 @@ struct DiscordWebhook { pub avatar_url: Option, pub username: Option, pub embeds: Vec, + pub content: Option, } const PLUGIN_LOADERS: &[&str] = &[ @@ -72,6 +73,7 @@ pub async fn send_discord_webhook( project_id: ProjectId, pool: &PgPool, webhook_url: String, + message: Option, ) -> Result<(), ApiError> { let all_game_versions = GameVersion::list(pool).await?; @@ -257,6 +259,7 @@ pub async fn send_discord_webhook( ), username: Some("Modrinth Release".to_string()), embeds: vec![embed], + content: message, }) .send() .await