Lots of little fixes (#73)
* Lots of little fixes * Change + Add TODOs back that were incomplete * Fix migrations * Run prepare * Minor fixes * Fix formatting * SQLX Prepare
This commit is contained in:
parent
1072d1306b
commit
ad29f2477e
15
migrations/20201014165954_create-statuses.sql
Normal file
15
migrations/20201014165954_create-statuses.sql
Normal file
@ -0,0 +1,15 @@
|
||||
CREATE TABLE statuses (
|
||||
id serial PRIMARY KEY UNIQUE NOT NULL,
|
||||
status varchar(64) UNIQUE NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE mods
|
||||
ADD COLUMN status integer REFERENCES statuses NOT NULL;
|
||||
ALTER TABLE mods
|
||||
ADD COLUMN updated timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
INSERT INTO statuses (status) VALUES ('approved');
|
||||
INSERT INTO statuses (status) VALUES ('rejected');
|
||||
INSERT INTO statuses (status) VALUES ('draft');
|
||||
INSERT INTO statuses (status) VALUES ('unlisted');
|
||||
INSERT INTO statuses (status) VALUES ('processing');
|
||||
415
sqlx-data.json
415
sqlx-data.json
@ -1,5 +1,45 @@
|
||||
{
|
||||
"db": "PostgreSQL",
|
||||
"01858339ec93e444b83c5d8793285d7b03982344770221c8a5a9bab9958d78ff": {
|
||||
"query": "\n SELECT id\n FROM release_channels\n WHERE channel = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"02d8895627dfe108735a6e10ad63239348b71b3322b4734526a2646f17aedf05": {
|
||||
"query": "\n SELECT status FROM statuses\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "status",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"03209c5bda2d704e688439919a7b3903db6ad7caebf7ddafb3ea52d312d47bfb": {
|
||||
"query": "\n INSERT INTO users (\n id, github_id, username, name, email,\n avatar_url, bio, created\n )\n VALUES (\n $1, $2, $3, $4, $5,\n $6, $7, $8\n )\n ",
|
||||
"describe": {
|
||||
@ -251,48 +291,8 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"1ffce9b2d5c9fa6c8b9abce4bad9f9419c44ad6367b7463b979c91b9b5b4fea1": {
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM versions WHERE id=$1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"25131559cb73a088000ab6379a769233440ade6c7511542da410065190d203fc": {
|
||||
"query": "\n SELECT id FROM loaders\n WHERE loader = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"268efd8c8145bbb2bc70bd5ae0a5e6f90c5917ffd18575afcdeaf78b4f895d1e": {
|
||||
"query": "\n SELECT id, title, description, downloads,\n icon_url, body_url, published,\n issues_url, source_url, wiki_url,\n team_id\n FROM mods\n WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))\n ",
|
||||
"1d144ed2bfb98d93f0515bb663708f2f9eff26548a50383f4013ba80305f7ac5": {
|
||||
"query": "\n SELECT m.id, m.title, m.description, m.downloads, m.icon_url, m.body_url, m.published, m.updated, m.team_id FROM mods m\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@ -332,29 +332,17 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "issues_url",
|
||||
"type_info": "Varchar"
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "source_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "wiki_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
@ -364,9 +352,47 @@
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"1ffce9b2d5c9fa6c8b9abce4bad9f9419c44ad6367b7463b979c91b9b5b4fea1": {
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM versions WHERE id=$1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"25131559cb73a088000ab6379a769233440ade6c7511542da410065190d203fc": {
|
||||
"query": "\n SELECT id FROM loaders\n WHERE loader = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
@ -422,6 +448,26 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"3135db1c5309dac7580a731b2829397ae7bdd6c9a67b21e813f26a4f5aa251a9": {
|
||||
"query": "\n SELECT status FROM statuses\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "status",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"33fc96ac71cfa382991cfb153e89da1e9f43ebf5367c28b30c336b758222307b": {
|
||||
"query": "\n DELETE FROM loaders_versions\n WHERE loaders_versions.version_id = $1\n ",
|
||||
"describe": {
|
||||
@ -446,6 +492,26 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"40597b84607e77809c13ffa9c6b0b1674bd6378a4737a8f6118e91ae2ede7e4a": {
|
||||
"query": "\n SELECT id\n FROM release_channels\n WHERE channel = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"4411f2aefd43881450da34db81e826110ac86c3a6cef9fd6a3e9e341508d1f09": {
|
||||
"query": "\n SELECT id FROM versions\n WHERE mod_id = $1\n ",
|
||||
"describe": {
|
||||
@ -787,73 +853,91 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"96d7b2c8b7b69fc370bb1a2d4a449f972eb3893dad5d6c59e498663cfc93a5c3": {
|
||||
"query": "\n SELECT title, description, downloads,\n icon_url, body_url, published,\n issues_url, source_url, wiki_url,\n team_id\n FROM mods\n WHERE id = $1\n ",
|
||||
"9a41d6c1d5c250df6114157edf5621a88bc336c5c628ba89182ba999e0af3ba8": {
|
||||
"query": "\n SELECT id, title, description, downloads,\n icon_url, body_url, published,\n updated, status,\n issues_url, source_url, wiki_url,\n team_id\n FROM mods\n WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"ordinal": 2,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 3,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 4,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 5,
|
||||
"name": "body_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 6,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 7,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "status",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "issues_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 10,
|
||||
"name": "source_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 11,
|
||||
"name": "wiki_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 12,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@ -979,6 +1063,26 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"adecffb1c41f9ddddf60c7c3c5a6be129aa8c49c53a698c6f4c2ee9ba15d6347": {
|
||||
"query": "\n SELECT id\n FROM statuses\n WHERE status = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"b0e3d1c70b87bb54819e3fac04b684a9b857aeedb4dcb7cb400c2af0dbb12922": {
|
||||
"query": "\n DELETE FROM teams\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
@ -1182,6 +1286,33 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"c7ee59f31b20790b6e865b2ec14ecd9985cacb0a84e40f087d0f908033566166": {
|
||||
"query": "\n SELECT u.id, u.username FROM users u\n INNER JOIN team_members tm ON tm.role = $1\n WHERE tm.team_id = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"c82eb1b059b62444ab1d17e5a0bd7ef8acea4b05c6f3576c07d20c4ca7635a11": {
|
||||
"query": "\n INSERT INTO dependencies (dependent_id, dependency_id)\n VALUES ($1, $2)\n ",
|
||||
"describe": {
|
||||
@ -1593,60 +1724,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"efe1bc80203f608226fa33e44654b681cc4430cec63bf7cf09b5281ff8c1c437": {
|
||||
"query": "\n SELECT m.id, m.title, m.description, m.downloads, m.icon_url, m.body_url, m.published FROM mods m\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "body_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"f0db9d8606ccc2196a9cfafe0e7090dab42bf790f25e0469b8947fac1cf043d5": {
|
||||
"query": "\n SELECT version FROM game_versions\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
@ -1687,6 +1764,92 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"f78dac3d15be1ea0d0ed43a4beadc04ec00d8ba68be2bb68cbc3f2ebe5c93dbd": {
|
||||
"query": "\n SELECT title, description, downloads,\n icon_url, body_url, published,\n updated, status,\n issues_url, source_url, wiki_url,\n team_id\n FROM mods\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "body_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "status",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "issues_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "source_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "wiki_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"f7bea04e8e279e27a24de1bdf3c413daa8677994df5131494b28691ed6611efc": {
|
||||
"query": "\n SELECT url,expires FROM states\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
|
||||
@ -102,6 +102,9 @@ pub struct TeamMemberId(pub i64);
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct ModId(pub i64);
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct StatusId(pub i32);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
#[sqlx(transparent)]
|
||||
|
||||
@ -12,6 +12,7 @@ pub struct ModBuilder {
|
||||
pub wiki_url: Option<String>,
|
||||
pub categories: Vec<CategoryId>,
|
||||
pub initial_versions: Vec<super::version_item::VersionBuilder>,
|
||||
pub status: StatusId,
|
||||
}
|
||||
|
||||
impl ModBuilder {
|
||||
@ -26,6 +27,8 @@ impl ModBuilder {
|
||||
description: self.description,
|
||||
body_url: self.body_url,
|
||||
published: chrono::Utc::now(),
|
||||
updated: chrono::Utc::now(),
|
||||
status: self.status,
|
||||
downloads: 0,
|
||||
icon_url: self.icon_url,
|
||||
issues_url: self.issues_url,
|
||||
@ -63,6 +66,8 @@ pub struct Mod {
|
||||
pub description: String,
|
||||
pub body_url: String,
|
||||
pub published: chrono::DateTime<chrono::Utc>,
|
||||
pub updated: chrono::DateTime<chrono::Utc>,
|
||||
pub status: StatusId,
|
||||
pub downloads: i32,
|
||||
pub icon_url: Option<String>,
|
||||
pub issues_url: Option<String>,
|
||||
@ -114,6 +119,7 @@ impl Mod {
|
||||
"
|
||||
SELECT title, description, downloads,
|
||||
icon_url, body_url, published,
|
||||
updated, status,
|
||||
issues_url, source_url, wiki_url,
|
||||
team_id
|
||||
FROM mods
|
||||
@ -134,9 +140,11 @@ impl Mod {
|
||||
body_url: row.body_url,
|
||||
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,
|
||||
status: StatusId(row.status),
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
@ -154,6 +162,7 @@ impl Mod {
|
||||
"
|
||||
SELECT id, title, description, downloads,
|
||||
icon_url, body_url, published,
|
||||
updated, status,
|
||||
issues_url, source_url, wiki_url,
|
||||
team_id
|
||||
FROM mods
|
||||
@ -172,9 +181,11 @@ impl Mod {
|
||||
body_url: m.body_url,
|
||||
icon_url: m.icon_url,
|
||||
published: m.published,
|
||||
updated: m.updated,
|
||||
issues_url: m.issues_url,
|
||||
source_url: m.source_url,
|
||||
wiki_url: m.wiki_url,
|
||||
status: StatusId(m.status),
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<Mod>>()
|
||||
|
||||
@ -31,6 +31,10 @@ pub struct Mod {
|
||||
pub body_url: String,
|
||||
/// The date at which the mod was first published.
|
||||
pub published: DateTime<Utc>,
|
||||
/// The date at which the mod was first published.
|
||||
pub updated: DateTime<Utc>,
|
||||
/// The status of the mod
|
||||
pub status: ModStatus,
|
||||
|
||||
/// The total number of downloads the mod has had.
|
||||
pub downloads: u32,
|
||||
@ -48,6 +52,49 @@ pub struct Mod {
|
||||
pub wiki_url: Option<String>,
|
||||
}
|
||||
|
||||
/// A status decides the visbility of a mod in search, URLs, and the whole site itself.
|
||||
/// Approved - Mod is displayed on search, and accessible by URL
|
||||
/// Rejected - Mod is not displayed on search, and not accessible by URL (Temporary state, mod can reapply)
|
||||
/// Draft - Mod is not displayed on search, and not accessible by URL
|
||||
/// Unlisted - Mod is not displayed on search, but accessible by URL
|
||||
/// Processing - Mod is not displayed on search, and not accessible by URL (Temporary state, mod under review)
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ModStatus {
|
||||
Approved,
|
||||
Rejected,
|
||||
Draft,
|
||||
Unlisted,
|
||||
Processing,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ModStatus {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
ModStatus::Approved => write!(fmt, "release"),
|
||||
ModStatus::Rejected => write!(fmt, "beta"),
|
||||
ModStatus::Draft => write!(fmt, "alpha"),
|
||||
ModStatus::Unlisted => write!(fmt, "unlisted"),
|
||||
ModStatus::Processing => write!(fmt, "Processing"),
|
||||
ModStatus::Unknown => write!(fmt, "Unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModStatus {
|
||||
pub fn from_str(string: &str) -> ModStatus {
|
||||
match string {
|
||||
"processing" => ModStatus::Processing,
|
||||
"rejected" => ModStatus::Processing,
|
||||
"approved" => ModStatus::Processing,
|
||||
"draft" => ModStatus::Processing,
|
||||
"unlisted" => ModStatus::Processing,
|
||||
_ => ModStatus::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A specific version of a mod
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Version {
|
||||
@ -128,7 +175,7 @@ pub struct SearchRequest {
|
||||
pub query: Option<String>,
|
||||
/// Must match a json 2 deep array of strings `[["categories:misc"]]`
|
||||
// TODO: We may want to have a better representation of this, so that
|
||||
// we are less likely to break backwards compatability
|
||||
// we are less likely to break backwards compatibility
|
||||
pub facets: Option<String>,
|
||||
pub filters: Option<String>,
|
||||
pub version: Option<String>,
|
||||
|
||||
@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize};
|
||||
#[serde(into = "Base62Id")]
|
||||
pub struct TeamId(pub u64);
|
||||
|
||||
pub const OWNER_ROLE: &str = "Owner";
|
||||
|
||||
// TODO: permissions, role names, etc
|
||||
/// A team of users who control a mod
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
||||
@ -6,8 +6,7 @@ pub async fn index_get() -> HttpResponse {
|
||||
let data = json!({
|
||||
"name": "modrinth-labrinth",
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
//TODO: Add the documentation link
|
||||
"documentation": "Nowhere yet",
|
||||
"documentation": "https://modrinth.com/documentation",
|
||||
"about": "Welcome traveler !"
|
||||
});
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use crate::auth::{get_user_from_headers, AuthenticationError};
|
||||
use crate::database::models;
|
||||
use crate::database::models::StatusId;
|
||||
use crate::file_hosting::{FileHost, FileHostingError};
|
||||
use crate::models::error::ApiError;
|
||||
use crate::models::mods::{ModId, VersionId, VersionType};
|
||||
use crate::models::mods::{ModId, ModStatus, VersionId};
|
||||
use crate::models::teams::TeamMember;
|
||||
use crate::models::users::UserId;
|
||||
use crate::routes::version_creation::InitialVersionData;
|
||||
@ -97,8 +98,6 @@ impl actix_web::ResponseError for CreateError {
|
||||
struct ModCreateData {
|
||||
/// The title or name of the mod.
|
||||
pub mod_name: String,
|
||||
/// The namespace of the mod
|
||||
pub mod_namespace: String,
|
||||
/// A short description of the mod.
|
||||
pub mod_description: String,
|
||||
/// A long description of the mod, in markdown.
|
||||
@ -212,6 +211,9 @@ async fn mod_create_inner(
|
||||
CreateError::InvalidInput(String::from("`data` field must come before file fields"))
|
||||
})?;
|
||||
|
||||
check_length("mod_name", 3, 255, &*create_data.mod_name)?;
|
||||
check_length("mod_description", 3, 2048, &*create_data.mod_description)?;
|
||||
|
||||
let (file_name, file_extension) =
|
||||
super::version_creation::get_name_ext(&content_disposition)?;
|
||||
|
||||
@ -267,12 +269,19 @@ async fn mod_create_inner(
|
||||
file_name: uploaded_text.file_name.clone(),
|
||||
});
|
||||
|
||||
// TODO: do a real lookup for the channels
|
||||
let release_channel = match version_data.release_channel {
|
||||
VersionType::Release => models::ChannelId(1),
|
||||
VersionType::Beta => models::ChannelId(3),
|
||||
VersionType::Alpha => models::ChannelId(5),
|
||||
};
|
||||
let release_channel = models::ChannelId(
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT id
|
||||
FROM release_channels
|
||||
WHERE channel = $1
|
||||
",
|
||||
version_data.release_channel.to_string()
|
||||
)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?
|
||||
.id,
|
||||
);
|
||||
|
||||
let mut game_versions = Vec::with_capacity(version_data.game_versions.len());
|
||||
for v in &version_data.game_versions {
|
||||
@ -379,7 +388,18 @@ async fn mod_create_inner(
|
||||
|
||||
let team_id = team.insert(&mut *transaction).await?;
|
||||
|
||||
// Insert the new mod into the database
|
||||
let status = ModStatus::Processing;
|
||||
let status_id = sqlx::query!(
|
||||
"
|
||||
SELECT id
|
||||
FROM statuses
|
||||
WHERE status = $1
|
||||
",
|
||||
status.to_string()
|
||||
)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?
|
||||
.id;
|
||||
|
||||
let mod_builder = models::mod_item::ModBuilder {
|
||||
mod_id: mod_id.into(),
|
||||
@ -394,6 +414,7 @@ async fn mod_create_inner(
|
||||
|
||||
categories,
|
||||
initial_versions: created_versions,
|
||||
status: StatusId(status_id),
|
||||
};
|
||||
|
||||
let versions_list = mod_builder
|
||||
@ -410,7 +431,6 @@ async fn mod_create_inner(
|
||||
|
||||
let now = chrono::Utc::now();
|
||||
let timestamp = now.timestamp();
|
||||
let formatted = now.to_string();
|
||||
|
||||
let index_mod = crate::search::UploadSearchMod {
|
||||
mod_id: format!("local-{}", mod_id),
|
||||
@ -418,17 +438,16 @@ async fn mod_create_inner(
|
||||
description: mod_builder.description.clone(),
|
||||
categories: create_data.categories.clone(),
|
||||
versions: versions_list,
|
||||
page_url: mod_builder.body_url.clone(),
|
||||
page_url: format!("https://modrinth.com/mod/{}", mod_id),
|
||||
icon_url: mod_builder.icon_url.clone().unwrap(),
|
||||
author: user.username,
|
||||
author_url: format!("https://modrinth.com/user/{}", user.id),
|
||||
// TODO: latest version info
|
||||
latest_version: String::new(),
|
||||
downloads: 0,
|
||||
date_created: formatted.clone(),
|
||||
date_created: now,
|
||||
created_timestamp: timestamp,
|
||||
// TODO: store and return modified time
|
||||
date_modified: formatted,
|
||||
date_modified: now,
|
||||
modified_timestamp: timestamp,
|
||||
host: Cow::Borrowed("modrinth"),
|
||||
empty: Cow::Borrowed("{}{}{}"),
|
||||
@ -443,6 +462,8 @@ async fn mod_create_inner(
|
||||
description: mod_builder.description.clone(),
|
||||
body_url: mod_builder.body_url.clone(),
|
||||
published: now,
|
||||
updated: now,
|
||||
status,
|
||||
downloads: 0,
|
||||
categories: create_data.categories.clone(),
|
||||
versions: mod_builder
|
||||
@ -514,3 +535,19 @@ fn get_image_content_type(extension: &str) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_length(
|
||||
var_name: &str,
|
||||
min_length: usize,
|
||||
max_length: usize,
|
||||
string: &str,
|
||||
) -> Result<(), CreateError> {
|
||||
if string.len() > max_length || string.len() < min_length {
|
||||
Err(CreateError::InvalidInput(format!(
|
||||
"The {} must be between {} and {} characters; got {}.",
|
||||
var_name, string, min_length, max_length
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,15 +36,29 @@ pub async fn mods_get(
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
let mods: Vec<models::mods::Mod> = mods_data
|
||||
.into_iter()
|
||||
.map(|m| models::mods::Mod {
|
||||
let mut mods: Vec<models::mods::Mod> = Vec::new();
|
||||
for m in mods_data {
|
||||
let status = sqlx::query!(
|
||||
"
|
||||
SELECT status FROM statuses
|
||||
WHERE id = $1
|
||||
",
|
||||
m.status.0,
|
||||
)
|
||||
.fetch_one(&**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.status;
|
||||
|
||||
mods.push(models::mods::Mod {
|
||||
id: m.id.into(),
|
||||
team: m.team_id.into(),
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
body_url: m.body_url,
|
||||
published: m.published,
|
||||
updated: m.updated,
|
||||
status: models::mods::ModStatus::from_str(&*status),
|
||||
|
||||
downloads: m.downloads as u32,
|
||||
categories: vec![],
|
||||
@ -54,7 +68,7 @@ pub async fn mods_get(
|
||||
source_url: m.source_url,
|
||||
wiki_url: m.wiki_url,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().json(mods))
|
||||
}
|
||||
@ -71,6 +85,19 @@ pub async fn mod_get(
|
||||
|
||||
if let Some(data) = mod_data {
|
||||
let m = data.inner;
|
||||
|
||||
let status = sqlx::query!(
|
||||
"
|
||||
SELECT status FROM statuses
|
||||
WHERE id = $1
|
||||
",
|
||||
m.status.0,
|
||||
)
|
||||
.fetch_one(&**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.status;
|
||||
|
||||
let response = models::mods::Mod {
|
||||
id: m.id.into(),
|
||||
team: m.team_id.into(),
|
||||
@ -78,6 +105,8 @@ pub async fn mod_get(
|
||||
description: m.description,
|
||||
body_url: m.body_url,
|
||||
published: m.published,
|
||||
updated: m.updated,
|
||||
status: models::mods::ModStatus::from_str(&*status),
|
||||
|
||||
downloads: m.downloads as u32,
|
||||
categories: data.categories,
|
||||
|
||||
@ -68,6 +68,7 @@ pub async fn version_create(
|
||||
result
|
||||
}
|
||||
|
||||
/// TODO: Update mod timestamp when new version is created
|
||||
async fn version_create_inner(
|
||||
req: HttpRequest,
|
||||
mut payload: Multipart,
|
||||
@ -167,12 +168,35 @@ async fn version_create_inner(
|
||||
file_name: uploaded_text.file_name.clone(),
|
||||
});
|
||||
|
||||
// TODO: do a real lookup for the channels
|
||||
let release_channel = match version_create_data.release_channel {
|
||||
VersionType::Release => models::ChannelId(1),
|
||||
VersionType::Beta => models::ChannelId(3),
|
||||
VersionType::Alpha => models::ChannelId(5),
|
||||
};
|
||||
let release_channel = models::ChannelId(
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT id
|
||||
FROM release_channels
|
||||
WHERE channel = $1
|
||||
",
|
||||
version_create_data.release_channel.to_string()
|
||||
)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?
|
||||
.id,
|
||||
);
|
||||
|
||||
let mut game_versions = Vec::with_capacity(version_create_data.game_versions.len());
|
||||
for v in &version_create_data.game_versions {
|
||||
let id = models::categories::GameVersion::get_id(&v.0, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| CreateError::InvalidGameVersion(v.0.clone()))?;
|
||||
game_versions.push(id);
|
||||
}
|
||||
|
||||
let mut loaders = Vec::with_capacity(version_create_data.loaders.len());
|
||||
for l in &version_create_data.loaders {
|
||||
let id = models::categories::Loader::get_id(&l.0, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| CreateError::InvalidLoader(l.0.clone()))?;
|
||||
loaders.push(id);
|
||||
}
|
||||
|
||||
version_builder = Some(VersionBuilder {
|
||||
version_id: version_id.into(),
|
||||
@ -187,9 +211,8 @@ async fn version_create_inner(
|
||||
.iter()
|
||||
.map(|x| (*x).into())
|
||||
.collect::<Vec<_>>(),
|
||||
// TODO: add game_versions and loaders info
|
||||
game_versions: vec![],
|
||||
loaders: vec![],
|
||||
game_versions,
|
||||
loaders,
|
||||
release_channel,
|
||||
});
|
||||
|
||||
|
||||
@ -197,9 +197,9 @@ pub async fn index_curseforge(
|
||||
page_url: curseforge_mod.website_url,
|
||||
icon_url,
|
||||
author_url: (&curseforge_mod.authors[0].url).to_string(),
|
||||
date_created: created.to_rfc3339(),
|
||||
date_created: created,
|
||||
created_timestamp: created.timestamp(),
|
||||
date_modified: modified.to_rfc3339(),
|
||||
date_modified: modified,
|
||||
modified_timestamp: modified.timestamp(),
|
||||
latest_version,
|
||||
host: Cow::Borrowed("curseforge"),
|
||||
|
||||
@ -6,6 +6,7 @@ use crate::search::UploadSearchMod;
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::borrow::Cow;
|
||||
|
||||
// TODO: only loaders for recent versions? For mods that have moved from forge to fabric
|
||||
pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingError> {
|
||||
info!("Indexing local mods!");
|
||||
|
||||
@ -13,10 +14,9 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
|
||||
let mut results = sqlx::query!(
|
||||
"
|
||||
SELECT m.id, m.title, m.description, m.downloads, m.icon_url, m.body_url, m.published FROM mods m
|
||||
SELECT m.id, m.title, m.description, m.downloads, m.icon_url, m.body_url, m.published, m.updated, m.team_id FROM mods m
|
||||
"
|
||||
)
|
||||
.fetch(&pool);
|
||||
).fetch(&pool);
|
||||
|
||||
while let Some(result) = results.next().await {
|
||||
if let Ok(result) = result {
|
||||
@ -34,7 +34,6 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
.try_collect::<Vec<String>>()
|
||||
.await?;
|
||||
|
||||
// TODO: only loaders for recent versions? For mods that have moved from forge to fabric
|
||||
let loaders: Vec<String> = sqlx::query!(
|
||||
"
|
||||
SELECT loaders.loader FROM versions
|
||||
@ -65,14 +64,24 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
|
||||
categories.extend(loaders);
|
||||
|
||||
let user = sqlx::query!(
|
||||
"
|
||||
SELECT u.id, u.username FROM users u
|
||||
INNER JOIN team_members tm ON tm.role = $1
|
||||
WHERE tm.team_id = $2
|
||||
",
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
result.team_id,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
let mut icon_url = "".to_string();
|
||||
|
||||
if let Some(url) = result.icon_url {
|
||||
icon_url = url;
|
||||
}
|
||||
|
||||
let formatted = result.published.to_rfc3339();
|
||||
let timestamp = result.published.timestamp();
|
||||
docs_to_add.push(UploadSearchMod {
|
||||
mod_id: format!("local-{}", crate::models::ids::ModId(result.id as u64)),
|
||||
title: result.title,
|
||||
@ -80,14 +89,14 @@ pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchMod>, IndexingE
|
||||
categories,
|
||||
versions,
|
||||
downloads: result.downloads,
|
||||
page_url: result.body_url,
|
||||
page_url: format!("https://modrinth.com/mod/{}", result.id),
|
||||
icon_url,
|
||||
author: "".to_string(), // TODO: author/team info
|
||||
author_url: "".to_string(),
|
||||
date_created: formatted.clone(),
|
||||
created_timestamp: timestamp,
|
||||
date_modified: formatted,
|
||||
modified_timestamp: timestamp,
|
||||
author: user.username,
|
||||
author_url: format!("https://modrinth.com/user/{}", user.id),
|
||||
date_created: result.published,
|
||||
created_timestamp: result.published.timestamp(),
|
||||
date_modified: result.updated,
|
||||
modified_timestamp: result.updated.timestamp(),
|
||||
latest_version: "".to_string(), // TODO: Info about latest version
|
||||
host: Cow::Borrowed("modrinth"),
|
||||
empty: Cow::Borrowed("{}{}{}"),
|
||||
|
||||
@ -2,6 +2,7 @@ use crate::models::error::ApiError;
|
||||
use crate::models::mods::SearchRequest;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::HttpResponse;
|
||||
use chrono::{DateTime, Utc};
|
||||
use meilisearch_sdk::client::Client;
|
||||
use meilisearch_sdk::document::Document;
|
||||
use meilisearch_sdk::search::Query;
|
||||
@ -64,11 +65,11 @@ pub struct UploadSearchMod {
|
||||
pub latest_version: String,
|
||||
|
||||
/// RFC 3339 formatted creation date of the mod
|
||||
pub date_created: String,
|
||||
pub date_created: DateTime<Utc>,
|
||||
/// Unix timestamp of the creation date of the mod
|
||||
pub created_timestamp: i64,
|
||||
/// RFC 3339 formatted date/time of last major modification (update)
|
||||
pub date_modified: String,
|
||||
pub date_modified: DateTime<Utc>,
|
||||
/// Unix timestamp of the last major modification
|
||||
pub modified_timestamp: i64,
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user