From cf818767c90022a7dfbefd4e94042119d1b8dce0 Mon Sep 17 00:00:00 2001 From: Chloe Date: Fri, 31 Jul 2026 01:06:37 -0700 Subject: [PATCH 1/4] my new idea --- .../20260730233906_add_mod_statuses.down.sql | 7 ++ .../20260730233906_add_mod_statuses.up.sql | 38 ++++++ src/database/repository/mods.rs | 118 +++++++++++++++--- src/endpoints/mod_version_submissions.rs | 5 + src/endpoints/mod_versions.rs | 35 ++++-- src/endpoints/mods.rs | 76 +++++++---- src/types/models/mod.rs | 1 + src/types/models/mod_entity.rs | 59 ++++++++- src/types/models/mod_status.rs | 11 ++ 9 files changed, 295 insertions(+), 55 deletions(-) create mode 100644 migrations/20260730233906_add_mod_statuses.down.sql create mode 100644 migrations/20260730233906_add_mod_statuses.up.sql create mode 100644 src/types/models/mod_status.rs diff --git a/migrations/20260730233906_add_mod_statuses.down.sql b/migrations/20260730233906_add_mod_statuses.down.sql new file mode 100644 index 0000000..774d1d4 --- /dev/null +++ b/migrations/20260730233906_add_mod_statuses.down.sql @@ -0,0 +1,7 @@ +-- Add down migration script here + +drop index mods_status_id_idx; + +alter table mods drop column status_id; +drop table mod_statuses; +drop type mod_status; \ No newline at end of file diff --git a/migrations/20260730233906_add_mod_statuses.up.sql b/migrations/20260730233906_add_mod_statuses.up.sql new file mode 100644 index 0000000..a21a633 --- /dev/null +++ b/migrations/20260730233906_add_mod_statuses.up.sql @@ -0,0 +1,38 @@ +-- Add up migration script here + +create type mod_status as enum('default', 'archived', 'unlisted'); + +create table mod_statuses( + id serial primary key, + status mod_status not null default 'default', + info text, + updated_at timestamptz not null default now(), + mod_id TEXT not null, + actor_id integer, + locked BOOLEAN not null DEFAULT FALSE, + foreign key (mod_id) + references mods(id) + on delete cascade, + foreign key (actor_id) + references developers(id) + on delete set null +); + +create index mod_statuses_actor_id_idx on mod_statuses(actor_id); +create unique index mod_statuses_mod_id_idx on mod_statuses(mod_id); + +alter table mods add column status_id integer; + +insert into mod_statuses (mod_id) SELECT id as mod_id from mods; + +update mods set status_id = ms.id + from mod_statuses ms + where mods.id = ms.mod_id; + +alter table mods alter column status_id set not null; +alter table mods + add foreign key (status_id) + references mod_statuses(id) + deferrable; + +create index mods_status_id_idx on mods(status_id); \ No newline at end of file diff --git a/src/database/repository/mods.rs b/src/database/repository/mods.rs index 5191855..3d16219 100644 --- a/src/database/repository/mods.rs +++ b/src/database/repository/mods.rs @@ -1,6 +1,9 @@ use crate::{ database::DatabaseError, - types::{mod_json::ModJson, models::mod_entity::Mod}, + types::{ + mod_json::ModJson, + models::{mod_entity::Mod, mod_status::ModStatusEnum} + }, }; use chrono::{DateTime, Utc}; use sqlx::PgConnection; @@ -18,6 +21,9 @@ struct ModRecordGetOne { changelog: Option, created_at: DateTime, updated_at: DateTime, + status: ModStatusEnum, + #[sqlx(default)] + status_info: Option, } impl ModRecordGetOne { @@ -35,6 +41,8 @@ impl ModRecordGetOne { about: self.about.clone(), changelog: self.changelog.clone(), links: None, + status: self.status, + status_info: self.status_info, } } } @@ -51,11 +59,13 @@ pub async fn get_one( if include_md { sqlx::query_as!( ModRecordGetOne, - "SELECT + r#"SELECT m.id, m.repository, m.about, m.changelog, m.featured, - m.download_count, m.created_at, m.updated_at + m.download_count, m.created_at, m.updated_at, + ms.status AS "status: _", ms.info AS status_info FROM mods m - WHERE id = $1", + INNER JOIN mod_statuses ms ON ms.mod_id = m.id + WHERE m.id = $1"#, id ) .fetch_optional(conn) @@ -66,11 +76,13 @@ pub async fn get_one( } else { sqlx::query_as!( ModRecordGetOne, - "SELECT + r#"SELECT m.id, m.repository, NULL as about, NULL as changelog, m.featured, - m.download_count, m.created_at, m.updated_at + m.download_count, m.created_at, m.updated_at, + ms.status AS "status: _", NULL AS status_info FROM mods m - WHERE id = $1", + INNER JOIN mod_statuses ms ON ms.mod_id = m.id + WHERE m.id = $1"#, id ) .fetch_optional(conn) @@ -84,15 +96,20 @@ pub async fn get_one( /// Does NOT check if the target mod exists #[tracing::instrument(skip_all, fields(mod_id = %json.id))] pub async fn create(json: &ModJson, conn: &mut PgConnection) -> Result { - sqlx::query_as!( - ModRecordGetOne, + sqlx::query!("SET CONSTRAINTS mods_status_id_fkey DEFERRED") + .execute(&mut *conn) + .await + .inspect_err(|e| tracing::error!("{:?}", e))?; + + let record = sqlx::query!( "INSERT INTO mods ( id, repository, changelog, about, - image - ) VALUES ($1, $2, $3, $4, $5) + image, + status_id + ) VALUES ($1, $2, $3, $4, $5, 0) RETURNING id, repository, about, changelog, featured, @@ -104,11 +121,45 @@ pub async fn create(json: &ModJson, conn: &mut PgConnection) -> Result Result<(), D Ok(()) } + +#[tracing::instrument(skip_all, fields(mod_id = %id))] +pub async fn has_status( + id: &str, + status: ModStatusEnum, + pool: &mut PgConnection, +) -> Result { + sqlx::query_scalar!(r#"SELECT EXISTS( + SELECT 1 FROM mods m + INNER JOIN mod_statuses ms ON m.id = ms.mod_id + WHERE m.id = $1 AND ms.status = $2 + ) AS "exists!""#, + id, status as ModStatusEnum + ) + .fetch_one(&mut *pool) + .await + .inspect_err(|e| tracing::error!("{:?}", e)) + .map_err(|e| e.into()) +} + +#[tracing::instrument(skip_all, fields(mod_id = %id))] +pub async fn is_status_locked( + id: &str, + pool: &mut PgConnection, +) -> Result { + sqlx::query_scalar!(r#"SELECT EXISTS( + SELECT 1 FROM mods m + INNER JOIN mod_statuses ms ON m.id = ms.mod_id + WHERE m.id = $1 AND ms.locked = TRUE + ) AS "exists!""#, + id + ) + .fetch_one(&mut *pool) + .await + .inspect_err(|e| tracing::error!("{:?}", e)) + .map_err(|e| e.into()) +} \ No newline at end of file diff --git a/src/endpoints/mod_version_submissions.rs b/src/endpoints/mod_version_submissions.rs index 9b1954e..5da9ce5 100644 --- a/src/endpoints/mod_version_submissions.rs +++ b/src/endpoints/mod_version_submissions.rs @@ -7,6 +7,7 @@ use crate::extractors::auth::Auth; use crate::types::api::{ApiResponse, PaginatedData}; use crate::types::models::audit_actions::{AuditAction, AuditActionRow}; use crate::types::models::developer::Developer; +use crate::types::models::mod_status::ModStatusEnum; use crate::types::models::mod_version_status::ModVersionStatusEnum; use crate::types::models::mod_version_submission::{ CreateCommentPayload, ModVersionSubmission, ModVersionSubmissionAttachment, @@ -80,6 +81,10 @@ async fn check_submission_lock( return Ok(true); } + if mods::has_status(mod_id, ModStatusEnum::Archived, &mut *conn).await? { + return Ok(false); + } + let access_to_mod = developers::has_access_to_mod(dev.id, mod_id, &mut *conn).await?; let active_developer = developers::has_active_mod(dev.id, &mut *conn).await?; diff --git a/src/endpoints/mod_versions.rs b/src/endpoints/mod_versions.rs index 3d48b99..83f3d54 100644 --- a/src/endpoints/mod_versions.rs +++ b/src/endpoints/mod_versions.rs @@ -18,6 +18,7 @@ use crate::events::mod_created::{ use crate::mod_zip::{self, download_mod}; use crate::s3_worker::S3WorkerTask; use crate::types::models; +use crate::types::models::mod_status::ModStatusEnum; use crate::types::models::mod_version_submission::ModVersionSubmissionLock; use crate::webhook::discord::DiscordWebhook; use crate::{ @@ -323,6 +324,10 @@ pub async fn create_version( return Err(ApiError::Authorization); } + if the_mod.status == ModStatusEnum::Archived { + return Err(ApiError::BadRequest("Mod is archived".to_string())); + } + let versions = mod_versions::get_for_mod( &the_mod.id, Some(&[ @@ -462,20 +467,22 @@ pub async fn create_version( tx.commit().await?; if make_accepted { - let owner = developers::get_owner_for_mod(&version.mod_id, &mut pool) - .await? - .ok_or(ApiError::BadRequest("Mod doesn't have an owner".into()))?; + if the_mod.status != ModStatusEnum::Unlisted { + let owner = developers::get_owner_for_mod(&version.mod_id, &mut pool) + .await? + .ok_or(ApiError::BadRequest("Mod doesn't have an owner".into()))?; - NewModVersionAcceptedEvent { - id: version.mod_id.clone(), - name: version.name.clone(), - version: version.version.clone(), - owner, - verified: NewModVersionVerification::VerifiedDev, - base_url: data.app_url().to_string(), + NewModVersionAcceptedEvent { + id: version.mod_id.clone(), + name: version.name.clone(), + version: version.version.clone(), + owner, + verified: NewModVersionVerification::VerifiedDev, + base_url: data.app_url().to_string(), + } + .to_discord_webhook() + .send(data.http_client(), data.webhook_url()); } - .to_discord_webhook() - .send(data.http_client(), data.webhook_url()); data.send_s3_task(S3WorkerTask::UploadMod { data: bytes, @@ -571,6 +578,8 @@ pub async fn update_version( ) .await?; + let mod_status = the_mod.status; + if old_status == ModVersionStatusEnum::Pending && version.status == ModVersionStatusEnum::Accepted { @@ -638,7 +647,7 @@ pub async fn update_version( tx.commit().await?; - if payload.status == ModVersionStatusEnum::Accepted { + if payload.status == ModVersionStatusEnum::Accepted && mod_status != ModStatusEnum::Unlisted { let is_update = approved_count > 0; let owner = developers::get_owner_for_mod(&version.mod_id, &mut pool) diff --git a/src/endpoints/mods.rs b/src/endpoints/mods.rs index b6c622b..c36ac80 100644 --- a/src/endpoints/mods.rs +++ b/src/endpoints/mods.rs @@ -23,6 +23,7 @@ use crate::types::models::deprecations::Deprecation; use crate::types::models::mod_entity::{Mod, ModUpdate}; use crate::types::models::mod_gd_version::{GDVersionEnum, VerPlatform}; use crate::types::models::mod_link::ModLinks; +use crate::types::models::mod_status::ModStatusEnum; use crate::types::models::mod_version_status::ModVersionStatusEnum; use crate::webhook::discord::DiscordWebhook; use actix_web::{HttpResponse, Responder, get, post, put, web}; @@ -428,7 +429,10 @@ pub async fn get_logo( #[derive(Deserialize, ToSchema)] struct UpdateModPayload { - featured: bool, + featured: Option, + status: Option, + info: Option, + status_locked: Option } /// Update a mod (admin only) @@ -443,7 +447,7 @@ struct UpdateModPayload { responses( (status = 204, description = "Mod updated successfully"), (status = 401, description = "Unauthorized"), - (status = 403, description = "Forbidden - Admin only"), + (status = 403, description = "Forbidden - Fields being updated are admin only"), (status = 404, description = "Mod not found") ), security( @@ -459,40 +463,66 @@ pub async fn update_mod( auth: Auth, ) -> Result { let dev = auth.developer()?; - auth.check_admin()?; let mut pool = data.db().acquire().await?; - let mut tx = pool.begin().await?; let id = path.into_inner(); + let mut tx = pool.begin().await?; if !mods::exists(&id, &mut tx).await? { return Err(ApiError::NotFound("Mod not found".into())); } - let featured = mods::is_featured(&id, &mut tx).await?; + if let Some(payload_featured) = payload.featured { + auth.check_admin()?; - Mod::update_mod(&id, payload.featured, &mut tx).await?; + let featured = mods::is_featured(&id, &mut tx).await?; + + Mod::update_mod(&id, payload_featured, &mut tx).await?; + + if featured != payload_featured { + let item = Mod::get_one(&id, true, &mut tx).await?; + if let Some(item) = item + && let Some(owner) = developers::get_owner_for_mod(&id, &mut tx).await? + && let Some(ver) = item.versions.first() + { + ModFeaturedEvent { + id: item.id, + name: ver.name.clone(), + owner, + admin: dev.clone(), + base_url: data.app_url().to_string(), + featured: payload_featured, + } + .to_discord_webhook() + .send(data.http_client(), data.webhook_url()); + } + } + } - tx.commit().await?; + if let Some(payload_status) = payload.status { + let status_locked = mods::is_status_locked(&id, &mut tx).await?; + let updating_locked_fields = payload.status_locked.is_some() || payload.info.is_some(); - if featured != payload.featured { - let item = Mod::get_one(&id, true, &mut pool).await?; - if let Some(item) = item - && let Some(owner) = developers::get_owner_for_mod(&id, &mut pool).await? - && let Some(ver) = item.versions.first() - { - ModFeaturedEvent { - id: item.id, - name: ver.name.clone(), - owner, - admin: dev, - base_url: data.app_url().to_string(), - featured: payload.featured, - } - .to_discord_webhook() - .send(data.http_client(), data.webhook_url()); + if !dev.admin && (status_locked || updating_locked_fields) { + return Err(ApiError::Authorization); + } + + let dev_of_mod = developers::has_access_to_mod(dev.id, &id, &mut tx).await?; + if !dev.admin && !dev_of_mod { + return Err(ApiError::Authorization); } + + Mod::update_mod_status( + &id, + payload_status, + payload.info.as_deref(), + payload.status_locked.unwrap_or(status_locked), + &dev, + &mut tx, + ).await?; } + tx.commit().await?; + Ok(HttpResponse::NoContent()) } diff --git a/src/types/models/mod.rs b/src/types/models/mod.rs index 1fa103e..b1d9810 100644 --- a/src/types/models/mod.rs +++ b/src/types/models/mod.rs @@ -8,6 +8,7 @@ pub mod github_login_attempt; pub mod incompatibility; pub mod loader_version; pub mod mod_entity; +pub mod mod_status; pub mod mod_gd_version; pub mod mod_link; pub mod mod_version; diff --git a/src/types/models/mod_entity.rs b/src/types/models/mod_entity.rs index 55421ed..3bac14f 100644 --- a/src/types/models/mod_entity.rs +++ b/src/types/models/mod_entity.rs @@ -18,7 +18,12 @@ use crate::{ }, types::{ api::PaginatedData, - models::{mod_version::ModVersion, mod_version_status::ModVersionStatusEnum}, + models::{ + mod_version::ModVersion, + mod_version_status::ModVersionStatusEnum, + developer::Developer, + mod_status::ModStatusEnum, + }, serde::chrono_dt_secs, }, }; @@ -48,6 +53,8 @@ pub struct Mod { #[serde(with = "chrono_dt_secs")] pub updated_at: DateTime, pub links: Option, + pub status: ModStatusEnum, + pub status_info: Option, } #[derive(Serialize, Debug, ToSchema)] @@ -74,6 +81,7 @@ struct ModRecord { changelog: Option, created_at: DateTime, updated_at: DateTime, + status: ModStatusEnum, } #[derive(sqlx::FromRow)] @@ -103,6 +111,8 @@ struct ModRecordGetOne { mod_version_created_at: Option>, mod_version_updated_at: Option>, info: Option, + mod_status: ModStatusEnum, + mod_status_info: Option, } pub struct ModStats { @@ -238,6 +248,7 @@ impl Mod { // joins: only join tables if they are necessary builder.push(" INNER JOIN mod_versions mv ON m.id = mv.mod_id "); builder.push(" INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id "); + builder.push(" INNER JOIN mod_statuses ms ON ms.mod_id = m.id"); if gd.is_some() || platforms.is_some() { builder.push(" INNER JOIN mod_gd_versions mgv ON mgv.mod_id = mv.id "); @@ -270,6 +281,13 @@ impl Mod { } builder.push(" AND mvs.status = ").push_bind(status); + builder.push(" AND ms.status <> 'unlisted'"); + + let direct_search = search_str.as_ref().is_some_and(|x| !x.is_empty()) || developer.is_some(); + if !direct_search { + // hide archived mods from direct search + builder.push(" AND ms.status <> 'archived'"); + } if let Some(rp) = requires_patching { builder.push(" AND mv.requires_patching = ").push_bind(rp); @@ -348,10 +366,10 @@ impl Mod { let mut records_builder = sqlx::QueryBuilder::new( "SELECT q.id, q.repository, q.about, q.changelog, - q.download_count, q.featured, q.created_at, q.updated_at + q.download_count, q.featured, q.created_at, q.updated_at, q.status FROM ( SELECT DISTINCT ON (m.id) m.id, mv.name, m.repository, m.about, m.changelog, - m.download_count, m.featured, m.created_at, m.updated_at ", + m.download_count, m.featured, m.created_at, m.updated_at, ms.status ", ); core_query(&mut records_builder); @@ -434,6 +452,8 @@ impl Mod { about: None, changelog: None, links, + status: x.status, + status_info: None, }) }) .collect(); @@ -481,6 +501,8 @@ impl Mod { about: x.about, changelog: x.changelog, links, + status: x.status, + status_info: None, } }) .collect::>(); @@ -591,10 +613,12 @@ impl Mod { mv.id as version_id, mv.name, mv.description, mv.version, mv.download_link, mv.managed_download_link, mv.download_count as mod_version_download_count, mv.created_at as mod_version_created_at, mv.updated_at as mod_version_updated_at, mv.hash, format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as "geode!: _", - mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as "status: _", mvs.info + mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as "status: _", mvs.info, + ms.status AS "mod_status: _", ms.info AS mod_status_info FROM mods m INNER JOIN mod_versions mv ON m.id = mv.mod_id INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id + INNER JOIN mod_statuses ms ON ms.mod_id = m.id WHERE m.id = $1 AND ($2 = false OR mvs.status = 'accepted') ORDER BY mv.id DESC"#, @@ -671,6 +695,8 @@ impl Mod { about: records[0].about.clone(), changelog: records[0].changelog.clone(), links, + status: records[0].mod_status, + status_info: records[0].mod_status_info.clone(), }; Ok(Some(mod_entity)) } @@ -803,4 +829,29 @@ impl Mod { Ok(ret) } + + #[tracing::instrument(skip_all, fields(mod_id = %id, status = ?status))] + pub async fn update_mod_status( + id: &str, + status: ModStatusEnum, + info: Option<&str>, + locked: bool, + updated_by: &Developer, + pool: &mut PgConnection, + ) -> Result<(), DatabaseError> { + sqlx::query!("UPDATE mod_statuses + SET status = $1, + actor_id = $2, + info = $3, + locked = $4, + updated_at = NOW() + WHERE mod_id = $5", + status as ModStatusEnum, updated_by.id, info, locked, id + ) + .execute(&mut *pool) + .await + .inspect_err(|e| tracing::error!("{:?}", e)) + .map_err(|e| e.into()) + .map(|_| ()) + } } diff --git a/src/types/models/mod_status.rs b/src/types/models/mod_status.rs new file mode 100644 index 0000000..d0ac583 --- /dev/null +++ b/src/types/models/mod_status.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy, Hash, PartialEq, Eq, ToSchema)] +#[serde(rename_all = "lowercase")] +#[sqlx(rename_all = "lowercase", type_name = "mod_status")] +pub enum ModStatusEnum { + Default, + Archived, + Unlisted, +} From 86c54d19bf46ca6c8df4043eac55ecf9628b0998 Mon Sep 17 00:00:00 2001 From: Chloe Date: Fri, 31 Jul 2026 01:51:06 -0700 Subject: [PATCH 2/4] fix indentation --- src/types/models/mod_status.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/models/mod_status.rs b/src/types/models/mod_status.rs index d0ac583..f929e64 100644 --- a/src/types/models/mod_status.rs +++ b/src/types/models/mod_status.rs @@ -6,6 +6,6 @@ use utoipa::ToSchema; #[sqlx(rename_all = "lowercase", type_name = "mod_status")] pub enum ModStatusEnum { Default, - Archived, + Archived, Unlisted, } From e12fc47fb95db346c035a16eb6607c0cbb9fb368 Mon Sep 17 00:00:00 2001 From: Chloe Date: Fri, 31 Jul 2026 02:56:04 -0700 Subject: [PATCH 3/4] perform prepare --- ...12ede5cd9545c0874278148ab9e268b20b969.json | 29 +++++++++++++ ...c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json} | 39 +++++++++++++++++- ...001b2e94a405715248f8a3064f6cb87f901a.json} | 36 ++++++++++++++-- ...3733dc7c826dea02aeb06fd202c093d9902e0.json | 23 +++++++++++ ...5e2f48b760b8ee773531de1c1a36083590a84.json | 12 ++++++ ...b4696ac11b5da72b20653a1f847862bb7ad5f.json | 35 ++++++++++++++++ ...10f02b814dc887b71a0c995eafd0dd8b1426.json} | 4 +- ...ae6b200cee6f038f52e633b0dbba06852653f.json | 15 +++++++ ...5afd42551899edcef89ab4812339e195b4a6b.json | 12 ++++++ ...b444d55b6103ab73734a16d586490a147518.json} | 41 +++++++++++++++++-- ...5a726f91f39a6113306d9a29f77d2cd98186c.json | 28 +++++++++++++ 11 files changed, 264 insertions(+), 10 deletions(-) create mode 100644 .sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json rename .sqlx/{query-2894a3bf8b4f43496a3a1532ba1e630726cf78d354b586094b1ee8c16a6fe9ee.json => query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json} (85%) rename .sqlx/{query-a9131f0ab03fa27cc7e6531397dc1d9343a95ff30a648d9c9b21120651a1caaa.json => query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json} (68%) create mode 100644 .sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json create mode 100644 .sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json create mode 100644 .sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json rename .sqlx/{query-f98fe5c88a7a234cc0b68fb39c156a741fbad1d2e4682e1e748037bb08962912.json => query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json} (87%) create mode 100644 .sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json create mode 100644 .sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json rename .sqlx/{query-959d6a60bbb47b24bc86963c0063bdd1268c75d03563eaf78eec41752cc1a491.json => query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json} (68%) create mode 100644 .sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json diff --git a/.sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json b/.sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json new file mode 100644 index 0000000..5c827db --- /dev/null +++ b/.sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE mod_statuses\n SET status = $1,\n actor_id = $2,\n info = $3,\n locked = $4,\n updated_at = NOW()\n WHERE mod_id = $5", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + }, + "Int4", + "Text", + "Bool", + "Text" + ] + }, + "nullable": [] + }, + "hash": "06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969" +} diff --git a/.sqlx/query-2894a3bf8b4f43496a3a1532ba1e630726cf78d354b586094b1ee8c16a6fe9ee.json b/.sqlx/query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json similarity index 85% rename from .sqlx/query-2894a3bf8b4f43496a3a1532ba1e630726cf78d354b586094b1ee8c16a6fe9ee.json rename to .sqlx/query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json index 91dd8c2..d5677e0 100644 --- a/.sqlx/query-2894a3bf8b4f43496a3a1532ba1e630726cf78d354b586094b1ee8c16a6fe9ee.json +++ b/.sqlx/query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured, m.download_count as mod_download_count, m.created_at, m.updated_at,\n mv.id as version_id, mv.name, mv.description, mv.version, mv.download_link, mv.managed_download_link,\n mv.download_count as mod_version_download_count, mv.created_at as mod_version_created_at, mv.updated_at as mod_version_updated_at, mv.hash,\n format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as \"geode!: _\",\n mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as \"status: _\", mvs.info\n FROM mods m\n INNER JOIN mod_versions mv ON m.id = mv.mod_id\n INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id\n WHERE m.id = $1\n AND ($2 = false OR mvs.status = 'accepted')\n ORDER BY mv.id DESC", + "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured, m.download_count as mod_download_count, m.created_at, m.updated_at,\n mv.id as version_id, mv.name, mv.description, mv.version, mv.download_link, mv.managed_download_link,\n mv.download_count as mod_version_download_count, mv.created_at as mod_version_created_at, mv.updated_at as mod_version_updated_at, mv.hash,\n format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as \"geode!: _\",\n mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as \"status: _\", mvs.info,\n ms.status AS \"mod_status: _\", ms.info AS mod_status_info\n FROM mods m\n INNER JOIN mod_versions mv ON m.id = mv.mod_id\n INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id\n INNER JOIN mod_statuses ms ON ms.mod_id = m.id\n WHERE m.id = $1\n AND ($2 = false OR mvs.status = 'accepted')\n ORDER BY mv.id DESC", "describe": { "columns": [ { @@ -284,6 +284,39 @@ "name": "info" } } + }, + { + "ordinal": 25, + "name": "mod_status: _", + "type_info": { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + }, + "origin": { + "Table": { + "table": "mod_statuses", + "name": "status" + } + } + }, + { + "ordinal": 26, + "name": "mod_status_info", + "type_info": "Text", + "origin": { + "Table": { + "table": "mod_statuses", + "name": "info" + } + } } ], "parameters": { @@ -317,8 +350,10 @@ false, false, false, + true, + false, true ] }, - "hash": "2894a3bf8b4f43496a3a1532ba1e630726cf78d354b586094b1ee8c16a6fe9ee" + "hash": "1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3" } diff --git a/.sqlx/query-a9131f0ab03fa27cc7e6531397dc1d9343a95ff30a648d9c9b21120651a1caaa.json b/.sqlx/query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json similarity index 68% rename from .sqlx/query-a9131f0ab03fa27cc7e6531397dc1d9343a95ff30a648d9c9b21120651a1caaa.json rename to .sqlx/query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json index e7c2506..59c0292 100644 --- a/.sqlx/query-a9131f0ab03fa27cc7e6531397dc1d9343a95ff30a648d9c9b21120651a1caaa.json +++ b/.sqlx/query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n m.id, m.repository, NULL as about, NULL as changelog, m.featured,\n m.download_count, m.created_at, m.updated_at\n FROM mods m\n WHERE id = $1", + "query": "SELECT\n m.id, m.repository, NULL as about, NULL as changelog, m.featured,\n m.download_count, m.created_at, m.updated_at,\n ms.status AS \"status: _\", NULL AS status_info\n FROM mods m\n INNER JOIN mod_statuses ms ON ms.mod_id = m.id\n WHERE m.id = $1", "describe": { "columns": [ { @@ -80,6 +80,34 @@ "name": "updated_at" } } + }, + { + "ordinal": 8, + "name": "status: _", + "type_info": { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + }, + "origin": { + "Table": { + "table": "mod_statuses", + "name": "status" + } + } + }, + { + "ordinal": 9, + "name": "status_info", + "type_info": "Text", + "origin": "Expression" } ], "parameters": { @@ -95,8 +123,10 @@ false, false, false, - false + false, + false, + null ] }, - "hash": "a9131f0ab03fa27cc7e6531397dc1d9343a95ff30a648d9c9b21120651a1caaa" + "hash": "2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a" } diff --git a/.sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json b/.sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json new file mode 100644 index 0000000..0b001b9 --- /dev/null +++ b/.sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(\n SELECT 1 FROM mods m\n INNER JOIN mod_statuses ms ON m.id = ms.mod_id\n WHERE m.id = $1 AND ms.locked = TRUE\n ) AS \"exists!\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists!", + "type_info": "Bool", + "origin": "Expression" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0" +} diff --git a/.sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json b/.sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json new file mode 100644 index 0000000..5e10aad --- /dev/null +++ b/.sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "SET CONSTRAINTS mods_status_id_fkey DEFERRED", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84" +} diff --git a/.sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json b/.sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json new file mode 100644 index 0000000..45f1ba4 --- /dev/null +++ b/.sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(\n SELECT 1 FROM mods m\n INNER JOIN mod_statuses ms ON m.id = ms.mod_id\n WHERE m.id = $1 AND ms.status = $2\n ) AS \"exists!\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists!", + "type_info": "Bool", + "origin": "Expression" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + } + ] + }, + "nullable": [ + null + ] + }, + "hash": "588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f" +} diff --git a/.sqlx/query-f98fe5c88a7a234cc0b68fb39c156a741fbad1d2e4682e1e748037bb08962912.json b/.sqlx/query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json similarity index 87% rename from .sqlx/query-f98fe5c88a7a234cc0b68fb39c156a741fbad1d2e4682e1e748037bb08962912.json rename to .sqlx/query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json index df6a0a8..d5931bf 100644 --- a/.sqlx/query-f98fe5c88a7a234cc0b68fb39c156a741fbad1d2e4682e1e748037bb08962912.json +++ b/.sqlx/query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO mods (\n id,\n repository,\n changelog,\n about,\n image\n ) VALUES ($1, $2, $3, $4, $5)\n RETURNING\n id, repository, about,\n changelog, featured,\n download_count, created_at,\n updated_at", + "query": "INSERT INTO mods (\n id,\n repository,\n changelog,\n about,\n image,\n status_id\n ) VALUES ($1, $2, $3, $4, $5, 0)\n RETURNING\n id, repository, about,\n changelog, featured,\n download_count, created_at,\n updated_at", "describe": { "columns": [ { @@ -112,5 +112,5 @@ false ] }, - "hash": "f98fe5c88a7a234cc0b68fb39c156a741fbad1d2e4682e1e748037bb08962912" + "hash": "66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426" } diff --git a/.sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json b/.sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json new file mode 100644 index 0000000..ad9acd0 --- /dev/null +++ b/.sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE mods SET status_id = $1 WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Text" + ] + }, + "nullable": [] + }, + "hash": "7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f" +} diff --git a/.sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json b/.sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json new file mode 100644 index 0000000..bb25e6f --- /dev/null +++ b/.sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "SET CONSTRAINTS mods_status_id_fkey IMMEDIATE", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b" +} diff --git a/.sqlx/query-959d6a60bbb47b24bc86963c0063bdd1268c75d03563eaf78eec41752cc1a491.json b/.sqlx/query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json similarity index 68% rename from .sqlx/query-959d6a60bbb47b24bc86963c0063bdd1268c75d03563eaf78eec41752cc1a491.json rename to .sqlx/query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json index 6cf71c0..649812a 100644 --- a/.sqlx/query-959d6a60bbb47b24bc86963c0063bdd1268c75d03563eaf78eec41752cc1a491.json +++ b/.sqlx/query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured,\n m.download_count, m.created_at, m.updated_at\n FROM mods m\n WHERE id = $1", + "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured,\n m.download_count, m.created_at, m.updated_at,\n ms.status AS \"status: _\", ms.info AS status_info\n FROM mods m\n INNER JOIN mod_statuses ms ON ms.mod_id = m.id\n WHERE m.id = $1", "describe": { "columns": [ { @@ -90,6 +90,39 @@ "name": "updated_at" } } + }, + { + "ordinal": 8, + "name": "status: _", + "type_info": { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + }, + "origin": { + "Table": { + "table": "mod_statuses", + "name": "status" + } + } + }, + { + "ordinal": 9, + "name": "status_info", + "type_info": "Text", + "origin": { + "Table": { + "table": "mod_statuses", + "name": "info" + } + } } ], "parameters": { @@ -105,8 +138,10 @@ false, false, false, - false + false, + false, + true ] }, - "hash": "959d6a60bbb47b24bc86963c0063bdd1268c75d03563eaf78eec41752cc1a491" + "hash": "ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518" } diff --git a/.sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json b/.sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json new file mode 100644 index 0000000..382d13d --- /dev/null +++ b/.sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO mod_statuses (mod_id) VALUES ($1) RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4", + "origin": { + "Table": { + "table": "mod_statuses", + "name": "id" + } + } + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c" +} From 352dbe1ca2cbe0fd1c4fc8349189b0c22ff1459f Mon Sep 17 00:00:00 2001 From: Chloe Date: Tue, 4 Aug 2026 22:26:43 -0700 Subject: [PATCH 4/4] redo layout of mod statuses to not be a separate table --- ...5e2f48b760b8ee773531de1c1a36083590a84.json | 12 ---- ...4dc2fe6bb074a2766cbac68b07f2592f93d9.json} | 10 +-- ...ae6b200cee6f038f52e633b0dbba06852653f.json | 15 ---- ...923efe082b9e162f406536f438d0aa8e1a50.json} | 10 +-- ...6d78c9604eac5c8a95378edcc6ef43410b16.json} | 41 ++++++++++- ...9e71ee627208e9b346fa5101f0b0fb96f302.json} | 4 +- ...0183594185e5a763e3946f45fd512f9c86270.json | 28 ++++++++ ...179c8282c04aee5e85c8a8c161b2ce8f55f2.json} | 6 +- ...5afd42551899edcef89ab4812339e195b4a6b.json | 12 ---- ...7710ceab8e8e1d1d03658d6a802aaed5df7f.json} | 6 +- ...f1f2576fc308d39b95bb12c9fd276d63d4e6.json} | 4 +- ...5a726f91f39a6113306d9a29f77d2cd98186c.json | 28 -------- .../20260730233906_add_mod_statuses.down.sql | 10 ++- .../20260730233906_add_mod_statuses.up.sql | 31 +++----- src/database/repository/mods.rs | 72 ++++--------------- src/endpoints/mods.rs | 8 +++ src/types/models/mod_entity.rs | 45 ++++++++---- 17 files changed, 158 insertions(+), 184 deletions(-) delete mode 100644 .sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json rename .sqlx/{query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json => query-6b3265a168fb436018db26f164d24dc2fe6bb074a2766cbac68b07f2592f93d9.json} (87%) delete mode 100644 .sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json rename .sqlx/{query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json => query-95f89020fa1c0cf7c6efb65c7d09923efe082b9e162f406536f438d0aa8e1a50.json} (92%) rename .sqlx/{query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json => query-ad037bc14883dee2aac7c31346fa6d78c9604eac5c8a95378edcc6ef43410b16.json} (68%) rename .sqlx/{query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json => query-cf34dd72d792ebe6639b65b0cbd79e71ee627208e9b346fa5101f0b0fb96f302.json} (74%) create mode 100644 .sqlx/query-d4441790dde78a00677bee572620183594185e5a763e3946f45fd512f9c86270.json rename .sqlx/{query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json => query-dbcd4443d116fb6c101efad6d0a4179c8282c04aee5e85c8a8c161b2ce8f55f2.json} (61%) delete mode 100644 .sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json rename .sqlx/{query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json => query-ea6a98e28767884cec10a13ce45a7710ceab8e8e1d1d03658d6a802aaed5df7f.json} (89%) rename .sqlx/{query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json => query-f879070b03c4617a9058d3396ee0f1f2576fc308d39b95bb12c9fd276d63d4e6.json} (63%) delete mode 100644 .sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json diff --git a/.sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json b/.sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json deleted file mode 100644 index 5e10aad..0000000 --- a/.sqlx/query-53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SET CONSTRAINTS mods_status_id_fkey DEFERRED", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "53c75f49c92aba5582d18e974185e2f48b760b8ee773531de1c1a36083590a84" -} diff --git a/.sqlx/query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json b/.sqlx/query-6b3265a168fb436018db26f164d24dc2fe6bb074a2766cbac68b07f2592f93d9.json similarity index 87% rename from .sqlx/query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json rename to .sqlx/query-6b3265a168fb436018db26f164d24dc2fe6bb074a2766cbac68b07f2592f93d9.json index 649812a..83ca559 100644 --- a/.sqlx/query-ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518.json +++ b/.sqlx/query-6b3265a168fb436018db26f164d24dc2fe6bb074a2766cbac68b07f2592f93d9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured,\n m.download_count, m.created_at, m.updated_at,\n ms.status AS \"status: _\", ms.info AS status_info\n FROM mods m\n INNER JOIN mod_statuses ms ON ms.mod_id = m.id\n WHERE m.id = $1", + "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured,\n m.download_count, m.created_at, m.updated_at,\n m.status AS \"status: _\", m.status_info\n FROM mods m\n WHERE m.id = $1", "describe": { "columns": [ { @@ -108,7 +108,7 @@ }, "origin": { "Table": { - "table": "mod_statuses", + "table": "mods", "name": "status" } } @@ -119,8 +119,8 @@ "type_info": "Text", "origin": { "Table": { - "table": "mod_statuses", - "name": "info" + "table": "mods", + "name": "status_info" } } } @@ -143,5 +143,5 @@ true ] }, - "hash": "ef332e13a09ed15fc4b02067807ab444d55b6103ab73734a16d586490a147518" + "hash": "6b3265a168fb436018db26f164d24dc2fe6bb074a2766cbac68b07f2592f93d9" } diff --git a/.sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json b/.sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json deleted file mode 100644 index ad9acd0..0000000 --- a/.sqlx/query-7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE mods SET status_id = $1 WHERE id = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Text" - ] - }, - "nullable": [] - }, - "hash": "7f8c391eb1a2a0e3ac12d0598b9ae6b200cee6f038f52e633b0dbba06852653f" -} diff --git a/.sqlx/query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json b/.sqlx/query-95f89020fa1c0cf7c6efb65c7d09923efe082b9e162f406536f438d0aa8e1a50.json similarity index 92% rename from .sqlx/query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json rename to .sqlx/query-95f89020fa1c0cf7c6efb65c7d09923efe082b9e162f406536f438d0aa8e1a50.json index d5677e0..c8282f2 100644 --- a/.sqlx/query-1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3.json +++ b/.sqlx/query-95f89020fa1c0cf7c6efb65c7d09923efe082b9e162f406536f438d0aa8e1a50.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured, m.download_count as mod_download_count, m.created_at, m.updated_at,\n mv.id as version_id, mv.name, mv.description, mv.version, mv.download_link, mv.managed_download_link,\n mv.download_count as mod_version_download_count, mv.created_at as mod_version_created_at, mv.updated_at as mod_version_updated_at, mv.hash,\n format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as \"geode!: _\",\n mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as \"status: _\", mvs.info,\n ms.status AS \"mod_status: _\", ms.info AS mod_status_info\n FROM mods m\n INNER JOIN mod_versions mv ON m.id = mv.mod_id\n INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id\n INNER JOIN mod_statuses ms ON ms.mod_id = m.id\n WHERE m.id = $1\n AND ($2 = false OR mvs.status = 'accepted')\n ORDER BY mv.id DESC", + "query": "SELECT\n m.id, m.repository, m.about, m.changelog, m.featured, m.download_count as mod_download_count, m.created_at, m.updated_at,\n mv.id as version_id, mv.name, mv.description, mv.version, mv.download_link, mv.managed_download_link,\n mv.download_count as mod_version_download_count, mv.created_at as mod_version_created_at, mv.updated_at as mod_version_updated_at, mv.hash,\n format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as \"geode!: _\",\n mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as \"status: _\", mvs.info,\n m.status AS \"mod_status: _\", m.status_info AS mod_status_info\n FROM mods m\n INNER JOIN mod_versions mv ON m.id = mv.mod_id\n INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id\n WHERE m.id = $1\n AND ($2 = false OR mvs.status = 'accepted')\n ORDER BY mv.id DESC", "describe": { "columns": [ { @@ -302,7 +302,7 @@ }, "origin": { "Table": { - "table": "mod_statuses", + "table": "mods", "name": "status" } } @@ -313,8 +313,8 @@ "type_info": "Text", "origin": { "Table": { - "table": "mod_statuses", - "name": "info" + "table": "mods", + "name": "status_info" } } } @@ -355,5 +355,5 @@ true ] }, - "hash": "1464e6f2388cbbefe41ab7c467e0c308b73c7bd0e08fb07e3a171cd0ae6d6ab3" + "hash": "95f89020fa1c0cf7c6efb65c7d09923efe082b9e162f406536f438d0aa8e1a50" } diff --git a/.sqlx/query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json b/.sqlx/query-ad037bc14883dee2aac7c31346fa6d78c9604eac5c8a95378edcc6ef43410b16.json similarity index 68% rename from .sqlx/query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json rename to .sqlx/query-ad037bc14883dee2aac7c31346fa6d78c9604eac5c8a95378edcc6ef43410b16.json index d5931bf..95c7229 100644 --- a/.sqlx/query-66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426.json +++ b/.sqlx/query-ad037bc14883dee2aac7c31346fa6d78c9604eac5c8a95378edcc6ef43410b16.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO mods (\n id,\n repository,\n changelog,\n about,\n image,\n status_id\n ) VALUES ($1, $2, $3, $4, $5, 0)\n RETURNING\n id, repository, about,\n changelog, featured,\n download_count, created_at,\n updated_at", + "query": "INSERT INTO mods (\n id,\n repository,\n changelog,\n about,\n image\n ) VALUES ($1, $2, $3, $4, $5)\n RETURNING\n id, repository, about,\n changelog, featured,\n download_count, created_at,\n updated_at, status as \"status: _\",\n status_info", "describe": { "columns": [ { @@ -90,6 +90,39 @@ "name": "updated_at" } } + }, + { + "ordinal": 8, + "name": "status: _", + "type_info": { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + }, + "origin": { + "Table": { + "table": "mods", + "name": "status" + } + } + }, + { + "ordinal": 9, + "name": "status_info", + "type_info": "Text", + "origin": { + "Table": { + "table": "mods", + "name": "status_info" + } + } } ], "parameters": { @@ -109,8 +142,10 @@ false, false, false, - false + false, + false, + true ] }, - "hash": "66186328d37a03e0bc17364fa2be10f02b814dc887b71a0c995eafd0dd8b1426" + "hash": "ad037bc14883dee2aac7c31346fa6d78c9604eac5c8a95378edcc6ef43410b16" } diff --git a/.sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json b/.sqlx/query-cf34dd72d792ebe6639b65b0cbd79e71ee627208e9b346fa5101f0b0fb96f302.json similarity index 74% rename from .sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json rename to .sqlx/query-cf34dd72d792ebe6639b65b0cbd79e71ee627208e9b346fa5101f0b0fb96f302.json index 45f1ba4..fb517b1 100644 --- a/.sqlx/query-588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f.json +++ b/.sqlx/query-cf34dd72d792ebe6639b65b0cbd79e71ee627208e9b346fa5101f0b0fb96f302.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(\n SELECT 1 FROM mods m\n INNER JOIN mod_statuses ms ON m.id = ms.mod_id\n WHERE m.id = $1 AND ms.status = $2\n ) AS \"exists!\"", + "query": "SELECT EXISTS(\n SELECT 1 FROM mods m\n WHERE m.id = $1 AND m.status = $2\n ) AS \"exists!\"", "describe": { "columns": [ { @@ -31,5 +31,5 @@ null ] }, - "hash": "588edf3d01b00e2210aa1577b76b4696ac11b5da72b20653a1f847862bb7ad5f" + "hash": "cf34dd72d792ebe6639b65b0cbd79e71ee627208e9b346fa5101f0b0fb96f302" } diff --git a/.sqlx/query-d4441790dde78a00677bee572620183594185e5a763e3946f45fd512f9c86270.json b/.sqlx/query-d4441790dde78a00677bee572620183594185e5a763e3946f45fd512f9c86270.json new file mode 100644 index 0000000..5d3bcd3 --- /dev/null +++ b/.sqlx/query-d4441790dde78a00677bee572620183594185e5a763e3946f45fd512f9c86270.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE mods\n SET status = $1,\n status_info = $2,\n status_locked = $3\n WHERE id = $4", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "mod_status", + "kind": { + "Enum": [ + "default", + "archived", + "unlisted" + ] + } + } + }, + "Text", + "Bool", + "Text" + ] + }, + "nullable": [] + }, + "hash": "d4441790dde78a00677bee572620183594185e5a763e3946f45fd512f9c86270" +} diff --git a/.sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json b/.sqlx/query-dbcd4443d116fb6c101efad6d0a4179c8282c04aee5e85c8a8c161b2ce8f55f2.json similarity index 61% rename from .sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json rename to .sqlx/query-dbcd4443d116fb6c101efad6d0a4179c8282c04aee5e85c8a8c161b2ce8f55f2.json index 5c827db..82fc720 100644 --- a/.sqlx/query-06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969.json +++ b/.sqlx/query-dbcd4443d116fb6c101efad6d0a4179c8282c04aee5e85c8a8c161b2ce8f55f2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE mod_statuses\n SET status = $1,\n actor_id = $2,\n info = $3,\n locked = $4,\n updated_at = NOW()\n WHERE mod_id = $5", + "query": "INSERT INTO mod_status_logs\n (status, info, locked, actor_id, mod_id)\n VALUES\n ($1, $2, $3, $4, $5)", "describe": { "columns": [], "parameters": { @@ -17,13 +17,13 @@ } } }, - "Int4", "Text", "Bool", + "Int4", "Text" ] }, "nullable": [] }, - "hash": "06a4ce8197437b76cc6686c4cfd12ede5cd9545c0874278148ab9e268b20b969" + "hash": "dbcd4443d116fb6c101efad6d0a4179c8282c04aee5e85c8a8c161b2ce8f55f2" } diff --git a/.sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json b/.sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json deleted file mode 100644 index bb25e6f..0000000 --- a/.sqlx/query-e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SET CONSTRAINTS mods_status_id_fkey IMMEDIATE", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "e03e835c24369349bbec1639b7f5afd42551899edcef89ab4812339e195b4a6b" -} diff --git a/.sqlx/query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json b/.sqlx/query-ea6a98e28767884cec10a13ce45a7710ceab8e8e1d1d03658d6a802aaed5df7f.json similarity index 89% rename from .sqlx/query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json rename to .sqlx/query-ea6a98e28767884cec10a13ce45a7710ceab8e8e1d1d03658d6a802aaed5df7f.json index 59c0292..d402a91 100644 --- a/.sqlx/query-2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a.json +++ b/.sqlx/query-ea6a98e28767884cec10a13ce45a7710ceab8e8e1d1d03658d6a802aaed5df7f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n m.id, m.repository, NULL as about, NULL as changelog, m.featured,\n m.download_count, m.created_at, m.updated_at,\n ms.status AS \"status: _\", NULL AS status_info\n FROM mods m\n INNER JOIN mod_statuses ms ON ms.mod_id = m.id\n WHERE m.id = $1", + "query": "SELECT\n m.id, m.repository, NULL as about, NULL as changelog, m.featured,\n m.download_count, m.created_at, m.updated_at,\n m.status AS \"status: _\", NULL AS status_info\n FROM mods m\n WHERE m.id = $1", "describe": { "columns": [ { @@ -98,7 +98,7 @@ }, "origin": { "Table": { - "table": "mod_statuses", + "table": "mods", "name": "status" } } @@ -128,5 +128,5 @@ null ] }, - "hash": "2abe4cd1be35381a41369ced2e76001b2e94a405715248f8a3064f6cb87f901a" + "hash": "ea6a98e28767884cec10a13ce45a7710ceab8e8e1d1d03658d6a802aaed5df7f" } diff --git a/.sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json b/.sqlx/query-f879070b03c4617a9058d3396ee0f1f2576fc308d39b95bb12c9fd276d63d4e6.json similarity index 63% rename from .sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json rename to .sqlx/query-f879070b03c4617a9058d3396ee0f1f2576fc308d39b95bb12c9fd276d63d4e6.json index 0b001b9..ae45ac6 100644 --- a/.sqlx/query-3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0.json +++ b/.sqlx/query-f879070b03c4617a9058d3396ee0f1f2576fc308d39b95bb12c9fd276d63d4e6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS(\n SELECT 1 FROM mods m\n INNER JOIN mod_statuses ms ON m.id = ms.mod_id\n WHERE m.id = $1 AND ms.locked = TRUE\n ) AS \"exists!\"", + "query": "SELECT EXISTS(\n SELECT 1 FROM mods m\n WHERE m.id = $1 AND m.status_locked = TRUE\n ) AS \"exists!\"", "describe": { "columns": [ { @@ -19,5 +19,5 @@ null ] }, - "hash": "3f065288e13b327a83b3b6abeaa3733dc7c826dea02aeb06fd202c093d9902e0" + "hash": "f879070b03c4617a9058d3396ee0f1f2576fc308d39b95bb12c9fd276d63d4e6" } diff --git a/.sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json b/.sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json deleted file mode 100644 index 382d13d..0000000 --- a/.sqlx/query-fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO mod_statuses (mod_id) VALUES ($1) RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4", - "origin": { - "Table": { - "table": "mod_statuses", - "name": "id" - } - } - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "fdae277f888f7d8bd3915f146df5a726f91f39a6113306d9a29f77d2cd98186c" -} diff --git a/migrations/20260730233906_add_mod_statuses.down.sql b/migrations/20260730233906_add_mod_statuses.down.sql index 774d1d4..ab702ce 100644 --- a/migrations/20260730233906_add_mod_statuses.down.sql +++ b/migrations/20260730233906_add_mod_statuses.down.sql @@ -1,7 +1,11 @@ -- Add down migration script here -drop index mods_status_id_idx; +drop index mods_status_idx; -alter table mods drop column status_id; -drop table mod_statuses; +alter table mods + drop column status, + drop column status_info, + drop column status_locked; + +drop table mod_status_logs; drop type mod_status; \ No newline at end of file diff --git a/migrations/20260730233906_add_mod_statuses.up.sql b/migrations/20260730233906_add_mod_statuses.up.sql index a21a633..b26b150 100644 --- a/migrations/20260730233906_add_mod_statuses.up.sql +++ b/migrations/20260730233906_add_mod_statuses.up.sql @@ -2,14 +2,14 @@ create type mod_status as enum('default', 'archived', 'unlisted'); -create table mod_statuses( +create table mod_status_logs( id serial primary key, - status mod_status not null default 'default', - info text, - updated_at timestamptz not null default now(), mod_id TEXT not null, + performed_at timestamptz not null default now(), actor_id integer, - locked BOOLEAN not null DEFAULT FALSE, + status mod_status not null default 'default', + info text, + locked BOOLEAN, foreign key (mod_id) references mods(id) on delete cascade, @@ -18,21 +18,12 @@ create table mod_statuses( on delete set null ); -create index mod_statuses_actor_id_idx on mod_statuses(actor_id); -create unique index mod_statuses_mod_id_idx on mod_statuses(mod_id); - -alter table mods add column status_id integer; - -insert into mod_statuses (mod_id) SELECT id as mod_id from mods; - -update mods set status_id = ms.id - from mod_statuses ms - where mods.id = ms.mod_id; +create index mod_statuses_actor_id_idx on mod_status_logs(actor_id); +create index mod_statuses_mod_id_idx on mod_status_logs(mod_id); -alter table mods alter column status_id set not null; alter table mods - add foreign key (status_id) - references mod_statuses(id) - deferrable; + add column status mod_status not null default 'default', + add column status_info text, + add column status_locked boolean not null default false; -create index mods_status_id_idx on mods(status_id); \ No newline at end of file +create index mods_status_idx on mods(status); \ No newline at end of file diff --git a/src/database/repository/mods.rs b/src/database/repository/mods.rs index 3d16219..edead5e 100644 --- a/src/database/repository/mods.rs +++ b/src/database/repository/mods.rs @@ -62,9 +62,8 @@ pub async fn get_one( r#"SELECT m.id, m.repository, m.about, m.changelog, m.featured, m.download_count, m.created_at, m.updated_at, - ms.status AS "status: _", ms.info AS status_info + m.status AS "status: _", m.status_info FROM mods m - INNER JOIN mod_statuses ms ON ms.mod_id = m.id WHERE m.id = $1"#, id ) @@ -79,9 +78,8 @@ pub async fn get_one( r#"SELECT m.id, m.repository, NULL as about, NULL as changelog, m.featured, m.download_count, m.created_at, m.updated_at, - ms.status AS "status: _", NULL AS status_info + m.status AS "status: _", NULL AS status_info FROM mods m - INNER JOIN mod_statuses ms ON ms.mod_id = m.id WHERE m.id = $1"#, id ) @@ -96,70 +94,32 @@ pub async fn get_one( /// Does NOT check if the target mod exists #[tracing::instrument(skip_all, fields(mod_id = %json.id))] pub async fn create(json: &ModJson, conn: &mut PgConnection) -> Result { - sqlx::query!("SET CONSTRAINTS mods_status_id_fkey DEFERRED") - .execute(&mut *conn) - .await - .inspect_err(|e| tracing::error!("{:?}", e))?; - - let record = sqlx::query!( - "INSERT INTO mods ( + sqlx::query_as!( + ModRecordGetOne, + r#"INSERT INTO mods ( id, repository, changelog, about, - image, - status_id - ) VALUES ($1, $2, $3, $4, $5, 0) + image + ) VALUES ($1, $2, $3, $4, $5) RETURNING id, repository, about, changelog, featured, download_count, created_at, - updated_at", + updated_at, status as "status: _", + status_info"#, &json.id, json.repository, json.changelog, json.about, &vec![] ) - .fetch_one(&mut *conn) - .await - .inspect_err(|e| tracing::error!("{:?}", e))?; - - let status_id = sqlx::query_scalar!("INSERT INTO mod_statuses (mod_id) VALUES ($1) RETURNING id", &json.id) - .fetch_one(&mut *conn) - .await - .inspect_err(|e| tracing::error!("{:?}", e))?; - - sqlx::query!( - "UPDATE mods SET status_id = $1 WHERE id = $2", - status_id, - &json.id - ) - .execute(&mut *conn) + .fetch_one(conn) .await - .inspect_err(|e| tracing::error!("{:?}", e))?; - - sqlx::query!("SET CONSTRAINTS mods_status_id_fkey IMMEDIATE") - .execute(&mut *conn) - .await - .inspect_err(|e| tracing::error!("{:?}", e))?; - - Ok(Mod { - id: record.id, - repository: record.repository, - featured: record.featured, - download_count: record.download_count.into(), - versions: Default::default(), - tags: Default::default(), - developers: Default::default(), - created_at: record.created_at, - updated_at: record.updated_at, - about: record.about.clone(), - changelog: record.changelog.clone(), - links: None, - status: ModStatusEnum::Default, - status_info: None, - }) + .inspect_err(|e| tracing::error!("{:?}", e)) + .map_err(|e| e.into()) + .map(|x| x.into_mod()) } #[tracing::instrument(skip_all, fields(mod_id = %id, developer_id = %developer_id))] @@ -361,8 +321,7 @@ pub async fn has_status( ) -> Result { sqlx::query_scalar!(r#"SELECT EXISTS( SELECT 1 FROM mods m - INNER JOIN mod_statuses ms ON m.id = ms.mod_id - WHERE m.id = $1 AND ms.status = $2 + WHERE m.id = $1 AND m.status = $2 ) AS "exists!""#, id, status as ModStatusEnum ) @@ -379,8 +338,7 @@ pub async fn is_status_locked( ) -> Result { sqlx::query_scalar!(r#"SELECT EXISTS( SELECT 1 FROM mods m - INNER JOIN mod_statuses ms ON m.id = ms.mod_id - WHERE m.id = $1 AND ms.locked = TRUE + WHERE m.id = $1 AND m.status_locked = TRUE ) AS "exists!""#, id ) diff --git a/src/endpoints/mods.rs b/src/endpoints/mods.rs index c36ac80..c24d411 100644 --- a/src/endpoints/mods.rs +++ b/src/endpoints/mods.rs @@ -517,6 +517,14 @@ pub async fn update_mod( payload_status, payload.info.as_deref(), payload.status_locked.unwrap_or(status_locked), + &mut tx, + ).await?; + + Mod::create_mod_status_log( + &id, + payload_status, + payload.info.as_deref(), + payload.status_locked, &dev, &mut tx, ).await?; diff --git a/src/types/models/mod_entity.rs b/src/types/models/mod_entity.rs index 3bac14f..bd45c35 100644 --- a/src/types/models/mod_entity.rs +++ b/src/types/models/mod_entity.rs @@ -248,7 +248,6 @@ impl Mod { // joins: only join tables if they are necessary builder.push(" INNER JOIN mod_versions mv ON m.id = mv.mod_id "); builder.push(" INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id "); - builder.push(" INNER JOIN mod_statuses ms ON ms.mod_id = m.id"); if gd.is_some() || platforms.is_some() { builder.push(" INNER JOIN mod_gd_versions mgv ON mgv.mod_id = mv.id "); @@ -281,12 +280,12 @@ impl Mod { } builder.push(" AND mvs.status = ").push_bind(status); - builder.push(" AND ms.status <> 'unlisted'"); + builder.push(" AND m.status <> 'unlisted'"); let direct_search = search_str.as_ref().is_some_and(|x| !x.is_empty()) || developer.is_some(); if !direct_search { // hide archived mods from direct search - builder.push(" AND ms.status <> 'archived'"); + builder.push(" AND m.status <> 'archived'"); } if let Some(rp) = requires_patching { @@ -369,7 +368,7 @@ impl Mod { q.download_count, q.featured, q.created_at, q.updated_at, q.status FROM ( SELECT DISTINCT ON (m.id) m.id, mv.name, m.repository, m.about, m.changelog, - m.download_count, m.featured, m.created_at, m.updated_at, ms.status ", + m.download_count, m.featured, m.created_at, m.updated_at, m.status ", ); core_query(&mut records_builder); @@ -614,11 +613,10 @@ impl Mod { mv.download_count as mod_version_download_count, mv.created_at as mod_version_created_at, mv.updated_at as mod_version_updated_at, mv.hash, format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as "geode!: _", mv.early_load, mv.requires_patching, mv.api, mv.mod_id, mvs.status as "status: _", mvs.info, - ms.status AS "mod_status: _", ms.info AS mod_status_info + m.status AS "mod_status: _", m.status_info AS mod_status_info FROM mods m INNER JOIN mod_versions mv ON m.id = mv.mod_id INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id - INNER JOIN mod_statuses ms ON ms.mod_id = m.id WHERE m.id = $1 AND ($2 = false OR mvs.status = 'accepted') ORDER BY mv.id DESC"#, @@ -836,17 +834,36 @@ impl Mod { status: ModStatusEnum, info: Option<&str>, locked: bool, - updated_by: &Developer, pool: &mut PgConnection, ) -> Result<(), DatabaseError> { - sqlx::query!("UPDATE mod_statuses + sqlx::query!("UPDATE mods SET status = $1, - actor_id = $2, - info = $3, - locked = $4, - updated_at = NOW() - WHERE mod_id = $5", - status as ModStatusEnum, updated_by.id, info, locked, id + status_info = $2, + status_locked = $3 + WHERE id = $4", + status as ModStatusEnum, info, locked, id + ) + .execute(&mut *pool) + .await + .inspect_err(|e| tracing::error!("{:?}", e)) + .map_err(|e| e.into()) + .map(|_| ()) + } + + #[tracing::instrument(skip_all, fields(mod_id = %id, status = ?status))] + pub async fn create_mod_status_log( + id: &str, + status: ModStatusEnum, + info: Option<&str>, + locked: Option, + updated_by: &Developer, + pool: &mut PgConnection, + ) -> Result<(), DatabaseError> { + sqlx::query!("INSERT INTO mod_status_logs + (status, info, locked, actor_id, mod_id) + VALUES + ($1, $2, $3, $4, $5)", + status as ModStatusEnum, info, locked, updated_by.id, id ) .execute(&mut *pool) .await