Skip to content

Commit a3e7cec

Browse files
committed
add support for unlisting mods
1 parent acda7ac commit a3e7cec

File tree

12 files changed

+348
-52
lines changed

12 files changed

+348
-52
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Add down migration script here
2+
3+
ALTER TABLE mods
4+
DROP IF EXISTS unlisted;
5+
6+
DROP TABLE IF EXISTS mod_unlist_history;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- Add up migration script here
2+
3+
ALTER TABLE mods
4+
ADD unlisted BOOLEAN DEFAULT FALSE NOT NULL;
5+
6+
CREATE TABLE mod_unlist_history(
7+
id BIGINT GENERATED ALWAYS AS IDENTITY,
8+
mod_id TEXT NOT NULL,
9+
unlisted BOOLEAN NOT NULL,
10+
details TEXT,
11+
modified_by INTEGER NOT NULL,
12+
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
13+
14+
FOREIGN KEY (mod_id) REFERENCES mods(id),
15+
FOREIGN KEY (modified_by) REFERENCES developers(id)
16+
);
17+
18+
CREATE INDEX idx_mod_unlist_history_mod_id ON mod_unlist_history(mod_id);

src/database/repository/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ pub mod mod_version_statuses;
1212
pub mod mod_versions;
1313
pub mod mods;
1414
pub mod refresh_tokens;
15+
pub mod mod_unlist_history;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use crate::database::DatabaseError;
2+
use crate::types::models::mod_unlist_history::ModUnlistHistory;
3+
use sqlx::PgConnection;
4+
5+
pub async fn create(
6+
mod_id: &str,
7+
unlisted: bool,
8+
details: Option<String>,
9+
modified_by: i32,
10+
conn: &mut PgConnection,
11+
) -> Result<ModUnlistHistory, DatabaseError> {
12+
sqlx::query_as!(
13+
ModUnlistHistory,
14+
"INSERT INTO mod_unlist_history
15+
(mod_id, unlisted, details, modified_by)
16+
VALUES ($1, $2, $3, $4)
17+
RETURNING
18+
id, mod_id, unlisted,
19+
details, modified_by, created_at",
20+
mod_id,
21+
unlisted,
22+
details,
23+
modified_by
24+
)
25+
.fetch_one(conn)
26+
.await
27+
.inspect_err(|e| log::error!("mod_unlist_history::create failed: {e}"))
28+
.map_err(|e| e.into())
29+
}
30+
31+
pub async fn get_last_for_mod(
32+
mod_id: &str,
33+
conn: &mut PgConnection,
34+
) -> Result<Option<ModUnlistHistory>, DatabaseError> {
35+
sqlx::query_as!(
36+
ModUnlistHistory,
37+
"SELECT
38+
id, mod_id, unlisted,
39+
details, modified_by, created_at
40+
FROM mod_unlist_history
41+
WHERE mod_id = $1
42+
ORDER BY id DESC
43+
LIMIT 1",
44+
mod_id,
45+
)
46+
.fetch_optional(conn)
47+
.await
48+
.inspect_err(|e| log::error!("mod_unlist_history::get_last_for_mod failed: {e}"))
49+
.map_err(|e| e.into())
50+
}
51+
52+
pub async fn get_for_mod(
53+
mod_id: &str,
54+
conn: &mut PgConnection,
55+
) -> Result<Vec<ModUnlistHistory>, DatabaseError> {
56+
sqlx::query_as!(
57+
ModUnlistHistory,
58+
"SELECT
59+
id, mod_id, unlisted,
60+
details, modified_by, created_at
61+
FROM mod_unlist_history
62+
WHERE mod_id = $1
63+
ORDER BY id DESC",
64+
mod_id,
65+
)
66+
.fetch_all(conn)
67+
.await
68+
.inspect_err(|e| log::error!("mod_unlist_history::get_for_mod failed: {e}"))
69+
.map_err(|e| e.into())
70+
}

src/database/repository/mod_versions.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ pub async fn get_for_mod(
9595
statuses: Option<&[ModVersionStatusEnum]>,
9696
conn: &mut PgConnection,
9797
) -> Result<Vec<ModVersion>, DatabaseError> {
98+
let unlisted = {
99+
if let Some(s) = statuses {
100+
ModVersionStatusEnum::get_unlisted_mod_filter_for_array(s)
101+
} else {
102+
None
103+
}
104+
};
105+
98106
sqlx::query_as!(
99107
ModVersionRow,
100108
r#"SELECT
@@ -105,12 +113,15 @@ pub async fn get_for_mod(
105113
mv.created_at, mv.updated_at,
106114
mvs.status as "status: _", mvs.info
107115
FROM mod_versions mv
116+
INNER JOIN mods m ON mv.mod_id = m.id
108117
INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id
109118
WHERE mv.mod_id = $1
110119
AND ($2::mod_version_status[] IS NULL OR mvs.status = ANY($2))
120+
AND ($3::bool IS NULL OR m.unlisted = $3)
111121
ORDER BY mv.id DESC"#,
112122
mod_id,
113-
statuses as Option<&[ModVersionStatusEnum]>
123+
statuses as Option<&[ModVersionStatusEnum]>,
124+
unlisted
114125
)
115126
.fetch_all(conn)
116127
.await
@@ -119,6 +130,47 @@ pub async fn get_for_mod(
119130
.map(|opt: Vec<ModVersionRow>| opt.into_iter().map(|x| x.into_mod_version()).collect())
120131
}
121132

133+
pub async fn get_latest_for_mod(
134+
mod_id: &str,
135+
statuses: Option<&[ModVersionStatusEnum]>,
136+
conn: &mut PgConnection,
137+
) -> Result<Option<ModVersion>, DatabaseError> {
138+
let unlisted = {
139+
if let Some(s) = statuses {
140+
ModVersionStatusEnum::get_unlisted_mod_filter_for_array(s)
141+
} else {
142+
None
143+
}
144+
};
145+
146+
sqlx::query_as!(
147+
ModVersionRow,
148+
r#"SELECT
149+
mv.id, mv.name, mv.description, mv.version,
150+
mv.download_link, mv.download_count, mv.hash,
151+
format_semver(mv.geode_major, mv.geode_minor, mv.geode_patch, mv.geode_meta) as "geode!: _",
152+
mv.early_load, mv.api, mv.mod_id,
153+
mv.created_at, mv.updated_at,
154+
mvs.status as "status: _", mvs.info
155+
FROM mod_versions mv
156+
INNER JOIN mod_version_statuses mvs ON mvs.mod_version_id = mv.id
157+
INNER JOIN mods m ON mv.mod_id = m.id
158+
WHERE mv.mod_id = $1
159+
AND ($2::mod_version_status[] IS NULL OR mvs.status = ANY($2))
160+
AND ($3::bool IS NOT NULL OR m.unlisted = $3)
161+
ORDER BY mv.id DESC
162+
LIMIT 1"#,
163+
mod_id,
164+
statuses as Option<&[ModVersionStatusEnum]>,
165+
unlisted
166+
)
167+
.fetch_optional(conn)
168+
.await
169+
.inspect_err(|e| log::error!("Failed to get latest mod_version for mod {mod_id}: {e}"))
170+
.map_err(|e| e.into())
171+
.map(|opt| opt.map(|x| x.into_mod_version()))
172+
}
173+
122174
pub async fn increment_downloads(id: i32, conn: &mut PgConnection) -> Result<(), DatabaseError> {
123175
sqlx::query!(
124176
"UPDATE mod_versions

src/database/repository/mods.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ use crate::{
33
types::{mod_json::ModJson, models::mod_entity::Mod},
44
};
55
use chrono::{DateTime, SecondsFormat, Utc};
6-
use sqlx::PgConnection;
6+
use sqlx::{PgConnection, Postgres, QueryBuilder};
77

88
#[derive(sqlx::FromRow)]
99
struct ModRecordGetOne {
1010
id: String,
1111
repository: Option<String>,
1212
featured: bool,
13+
unlisted: bool,
1314
download_count: i32,
1415
#[sqlx(default)]
1516
about: Option<String>,
@@ -25,6 +26,7 @@ impl ModRecordGetOne {
2526
id: self.id,
2627
repository: self.repository,
2728
featured: self.featured,
29+
unlisted: self.unlisted,
2830
download_count: self.download_count,
2931
versions: Default::default(),
3032
tags: Default::default(),
@@ -50,7 +52,7 @@ pub async fn get_one(
5052
sqlx::query_as!(
5153
ModRecordGetOne,
5254
"SELECT
53-
m.id, m.repository, m.about, m.changelog, m.featured,
55+
m.id, m.repository, m.about, m.changelog, m.featured, m.unlisted,
5456
m.download_count, m.created_at, m.updated_at
5557
FROM mods m
5658
WHERE id = $1",
@@ -65,7 +67,7 @@ pub async fn get_one(
6567
sqlx::query_as!(
6668
ModRecordGetOne,
6769
"SELECT
68-
m.id, m.repository, NULL as about, NULL as changelog, m.featured,
70+
m.id, m.repository, NULL as about, NULL as changelog, m.featured, m.unlisted,
6971
m.download_count, m.created_at, m.updated_at
7072
FROM mods m
7173
WHERE id = $1",
@@ -92,7 +94,7 @@ pub async fn create(json: &ModJson, conn: &mut PgConnection) -> Result<Mod, Data
9294
) VALUES ($1, $2, $3, $4, $5)
9395
RETURNING
9496
id, repository, about,
95-
changelog, featured,
97+
changelog, featured, unlisted,
9698
download_count, created_at,
9799
updated_at",
98100
&json.id,
@@ -234,11 +236,13 @@ pub async fn update_with_json(
234236
about = $2,
235237
changelog = $3,
236238
image = $4,
237-
updated_at = NOW()",
239+
updated_at = NOW()
240+
WHERE id = $5",
238241
json.repository,
239242
json.about,
240243
json.changelog,
241-
json.logo
244+
json.logo,
245+
the_mod.id
242246
)
243247
.execute(conn)
244248
.await
@@ -281,6 +285,48 @@ pub async fn update_with_json_moved(
281285
Ok(the_mod)
282286
}
283287

288+
pub async fn user_update(
289+
mut the_mod: Mod,
290+
featured: Option<bool>,
291+
unlisted: Option<bool>,
292+
pool: &mut PgConnection,
293+
) -> Result<Mod, DatabaseError> {
294+
if featured.is_none() && unlisted.is_none() {
295+
return Err(DatabaseError::InvalidInput(
296+
"No new fields were supplied".into(),
297+
));
298+
}
299+
300+
let mut builder: QueryBuilder<Postgres> = QueryBuilder::new("UPDATE mods SET ");
301+
let mut split = builder.separated(", ");
302+
303+
if let Some(featured) = featured {
304+
split.push("featured = ");
305+
split.push_bind(featured);
306+
}
307+
308+
if let Some(unlisted) = unlisted {
309+
split.push("unlisted = ");
310+
split.push_bind(unlisted);
311+
}
312+
313+
builder
314+
.build()
315+
.execute(&mut *pool)
316+
.await
317+
.inspect_err(|e| log::error!("Failed to update mod {}: {e}", the_mod.id))
318+
.map(|_| ())?;
319+
320+
if let Some(featured) = featured {
321+
the_mod.featured = featured;
322+
}
323+
if let Some(unlisted) = unlisted {
324+
the_mod.unlisted = unlisted;
325+
}
326+
327+
Ok(the_mod)
328+
}
329+
284330
/// Used when first version goes from pending to accepted.
285331
/// Makes it so versions that stay a lot in pending appear at the top of the newly created lists
286332
pub async fn touch_created_at(id: &str, conn: &mut PgConnection) -> Result<(), DatabaseError> {

0 commit comments

Comments
 (0)