Improve peformance of search indexing, v2 fixes + new routes (#205)
* Refactor search to not spam the database with queries, new utility routes for V2 * Run prepare
This commit is contained in:
parent
16db28060c
commit
157962e42a
786
sqlx-data.json
786
sqlx-data.json
@ -187,32 +187,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"07e72d55c2f18744bbfffee9920866d4aacd680f316058ec734735c173a7f16b": {
|
||||
"query": "\n SELECT DISTINCT gv.version, gv.created FROM versions\n INNER JOIN game_versions_versions gvv ON gvv.joining_version_id=versions.id\n INNER JOIN game_versions gv ON gvv.game_version_id=gv.id\n WHERE versions.mod_id = $1\n ORDER BY gv.created ASC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "version",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "created",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"07ebc9dc82cd012cd4f5880b1eb3d82602c195a3e3ddd557103ee037aa6dad1c": {
|
||||
"query": "\n INSERT INTO mods_donations (joining_mod_id, joining_platform_id, url)\n VALUES ($1, $2, $3)\n ",
|
||||
"describe": {
|
||||
@ -261,6 +235,147 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"0bea37e9f2c4c962633fd3359702974962d9f264593bc603c72da5bc7ddff4c4": {
|
||||
"query": "\n SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.published published,\n m.updated updated,\n m.team_id team_id, m.license license, m.slug slug,\n s.status status_name, cs.name client_side_type, ss.name server_side_type, l.short short, pt.name project_type_name, u.username username,\n STRING_AGG(DISTINCT c.category, ',') categories, STRING_AGG(DISTINCT lo.loader, ',') loaders, STRING_AGG(DISTINCT gv.version, ',') versions\n FROM mods m\n LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id\n LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id\n LEFT OUTER JOIN versions v ON v.mod_id = m.id\n INNER JOIN statuses s ON s.id = m.status\n INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id\n INNER JOIN game_versions gv ON gvv.game_version_id = gv.id\n INNER JOIN loaders_versions lv ON lv.version_id = v.id\n INNER JOIN loaders lo ON lo.id = lv.loader_id\n INNER JOIN project_types pt ON pt.id = m.project_type\n INNER JOIN side_types cs ON m.client_side = cs.id\n INNER JOIN side_types ss ON m.server_side = ss.id\n INNER JOIN licenses l ON m.license = l.id\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $2\n INNER JOIN users u ON tm.user_id = u.id\n WHERE s.status = $1\n GROUP BY m.id, s.id, cs.id, ss.id, l.id, pt.id, u.id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "project_type",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "follows",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "license",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "status_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "client_side_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "server_side_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "short",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "project_type_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "categories",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "loaders",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "versions",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"0ca11a32b2860e4f5c3d20892a5be3cb419e084f42ba0f98e09b9995027fcc4e": {
|
||||
"query": "\n SELECT id FROM statuses\n WHERE status = $1\n ",
|
||||
"describe": {
|
||||
@ -281,33 +396,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"0da158263c6588a83421154342db2ede16b9abf9931827790b9fcaf71080c324": {
|
||||
"query": "\n SELECT u.id, u.username FROM users u\n INNER JOIN team_members tm ON tm.user_id = u.id\n WHERE tm.team_id = $2 AND tm.role = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"0dbd0fa9a25416716a047184944d243ed5cb55808c6f300d7335c887f02a7f6e": {
|
||||
"query": "\n INSERT INTO report_types (name)\n VALUES ($1)\n ON CONFLICT (name) DO NOTHING\n RETURNING id\n ",
|
||||
"describe": {
|
||||
@ -1021,108 +1109,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"255ebd215adf4e2a5a837b8a682b7396d185c656e1151fcec24eddca1b7fb942": {
|
||||
"query": "\n SELECT m.id, m.title, m.description, m.downloads, m.follows, m.icon_url, m.body_url, m.published, m.updated, m.team_id, m.status, m.slug, m.license, m.client_side, m.server_side 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": "follows",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "body_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "status",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "license",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "client_side",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "server_side",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"29e657d26f0fb24a766f5b5eb6a94d01d1616884d8ca10e91536e974d5b585a6": {
|
||||
"query": "\n INSERT INTO loaders_versions (loader_id, version_id)\n VALUES ($1, $2)\n ",
|
||||
"describe": {
|
||||
@ -1178,26 +1164,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"31853f131eaeb2aaedc2bcc27da387462408409af810337f6a8ef397f674fb44": {
|
||||
"query": "\n SELECT DISTINCT loaders.loader FROM versions\n INNER JOIN loaders_versions lv ON lv.version_id = versions.id\n INNER JOIN loaders ON loaders.id = lv.loader_id\n WHERE versions.mod_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "loader",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"33fc96ac71cfa382991cfb153e89da1e9f43ebf5367c28b30c336b758222307b": {
|
||||
"query": "\n DELETE FROM loaders_versions\n WHERE loaders_versions.version_id = $1\n ",
|
||||
"describe": {
|
||||
@ -1539,26 +1505,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"3fdece422b1c54bc0853fa3ddbb4c1d1a45ac7723c906544ea9b4f69e5b29dc1": {
|
||||
"query": "\n SELECT name FROM side_types\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"413762398111e04074a2d8a1e4e03ed362b9167d397947f8d14e5ae330e3de0b": {
|
||||
"query": "\n UPDATE versions\n SET downloads = downloads + 1\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
@ -1571,26 +1517,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"43660c74ef6a72b3fc68006a2f743737f1e4973788a5c954ffeaac151c16d0c1": {
|
||||
"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
|
||||
]
|
||||
}
|
||||
},
|
||||
"43b793e2df30a6ace9e037e38bb4ea456656cfbe276c151e3a9e0a408d2c249f": {
|
||||
"query": "\n UPDATE versions\n SET release_channel = $1\n WHERE (id = $2)\n ",
|
||||
"describe": {
|
||||
@ -1926,26 +1852,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"4c99c0840159d18e88cd6094a41117258f2337346c145d926b5b610c76b5125f": {
|
||||
"query": "\n SELECT c.category\n FROM mods_categories mc\n INNER JOIN categories c ON mc.joining_category_id=c.id\n WHERE mc.joining_mod_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "category",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"4e9f9eafbfd705dfc94571018cb747245a98ea61bad3fae4b3ce284229d99955": {
|
||||
"query": "\n UPDATE mods\n SET description = $1\n WHERE (id = $2)\n ",
|
||||
"describe": {
|
||||
@ -2122,6 +2028,147 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"57d2a9f3dd9377fa7435deed1e09c474e447d7e502004556e239b6c2984e259b": {
|
||||
"query": "\n SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,\n m.icon_url icon_url, m.published published,\n m.updated updated,\n m.team_id team_id, m.license license, m.slug slug,\n s.status status_name, cs.name client_side_type, ss.name server_side_type, l.short short, pt.name project_type_name, u.username username,\n STRING_AGG(DISTINCT c.category, ',') categories, STRING_AGG(DISTINCT lo.loader, ',') loaders, STRING_AGG(DISTINCT gv.version, ',') versions\n FROM mods m\n LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id\n LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id\n LEFT OUTER JOIN versions v ON v.mod_id = m.id\n INNER JOIN statuses s ON s.id = m.status\n INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id\n INNER JOIN game_versions gv ON gvv.game_version_id = gv.id\n INNER JOIN loaders_versions lv ON lv.version_id = v.id\n INNER JOIN loaders lo ON lo.id = lv.loader_id\n INNER JOIN project_types pt ON pt.id = m.project_type\n INNER JOIN side_types cs ON m.client_side = cs.id\n INNER JOIN side_types ss ON m.server_side = ss.id\n INNER JOIN licenses l ON m.license = l.id\n INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $2\n INNER JOIN users u ON tm.user_id = u.id\n WHERE m.id = $1\n GROUP BY m.id, s.id, cs.id, ss.id, l.id, pt.id, u.id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "project_type",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "description",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "follows",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "license",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "status_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "client_side_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "server_side_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "short",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "project_type_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "categories",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "loaders",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "versions",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"5a13a79ebb1ab975f88b58e6deaba9685fe16e242c0fa4a5eea54f12f9448e6b": {
|
||||
"query": "\n DELETE FROM reports\n WHERE version_id = $1\n ",
|
||||
"describe": {
|
||||
@ -2646,26 +2693,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"72c1e6de8f2c8d89be030454eeab6d5c9695164af2ebfb8d7e94b2deee4f130d": {
|
||||
"query": "\n SELECT c.category\n FROM mods_categories mc\n INNER JOIN categories c ON mc.joining_category_id=c.id\n WHERE mc.joining_mod_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "category",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"72c75313688dfd88a659c5250c71b9899abd6186ab32a067a7d4b8a0846ebd18": {
|
||||
"query": "\n INSERT INTO game_versions (version, type, created)\n VALUES ($1, COALESCE($2, 'other'), COALESCE($3, timezone('utc', now())))\n ON CONFLICT (version) DO UPDATE\n SET type = COALESCE($2, game_versions.type),\n created = COALESCE($3, game_versions.created)\n RETURNING id\n ",
|
||||
"describe": {
|
||||
@ -3413,26 +3440,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"9985165594f04fb1d68e2e415a996b6553e8b5c91f121df3a9194806df10a197": {
|
||||
"query": "\n SELECT name FROM side_types\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"99a1eac69d7f5a5139703df431e6a5c3012a90143a8c635f93632f04d0bc41d4": {
|
||||
"query": "\n UPDATE mods\n SET wiki_url = $1\n WHERE (id = $2)\n ",
|
||||
"describe": {
|
||||
@ -3446,33 +3453,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"9d95d136d0e6eedee57e6aa524232c02609b89e4e26032e07403aabb69bea0d8": {
|
||||
"query": "\n SELECT u.id, u.username FROM users u\n INNER JOIN team_members tm ON tm.user_id = u.id\n WHERE tm.team_id = $2 AND tm.role = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"a39ce28b656032f862b205cffa393a76b989f4803654a615477a94fda5f57354": {
|
||||
"query": "\n DELETE FROM states\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
@ -3731,32 +3711,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"ad273daadd249e93b500c339a62aac48a497ebcc15164776ad20860a4d232896": {
|
||||
"query": "\n SELECT DISTINCT gv.version, gv.created FROM versions\n INNER JOIN game_versions_versions gvv ON gvv.joining_version_id=versions.id\n INNER JOIN game_versions gv ON gvv.game_version_id=gv.id\n WHERE versions.mod_id = $1\n ORDER BY gv.created ASC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "version",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "created",
|
||||
"type_info": "Timestamptz"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"b0e3d1c70b87bb54819e3fac04b684a9b857aeedb4dcb7cb400c2af0dbb12922": {
|
||||
"query": "\n DELETE FROM teams\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
@ -4752,13 +4706,73 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"d44c747044d9b1f6424e6a0eece324fd3e0964efdcb9ec7d18e70a2f67787914": {
|
||||
"query": "\n SELECT DISTINCT loaders.loader FROM versions\n INNER JOIN loaders_versions lv ON lv.version_id = versions.id\n INNER JOIN loaders ON loaders.id = lv.loader_id\n WHERE versions.mod_id = $1\n ",
|
||||
"d2bba2670ef992df166a5e1e4d90f14f1d6b19c5fe77eb7139a5e1a0e660f6db": {
|
||||
"query": "\n SELECT tm.id id, tm.role member_role, tm.permissions permissions, tm.accepted accepted,\n u.id user_id, u.github_id github_id, u.name user_name, u.email email,\n u.avatar_url avatar_url, u.username username, u.bio bio,\n u.created created, u.role user_role\n FROM team_members tm\n INNER JOIN users u ON u.id = tm.user_id\n WHERE tm.team_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "loader",
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "member_role",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "permissions",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "accepted",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "user_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "github_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "user_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "avatar_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "bio",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "created",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "user_role",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
@ -4768,6 +4782,18 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
@ -5464,104 +5490,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"fa5fe155fafd3b10c12fe5cf9cbbe4a4b3eb59f0f2e6584753a72517bb2d4574": {
|
||||
"query": "\n SELECT m.id, m.title, m.description, m.downloads, m.follows, m.icon_url, m.body_url, m.published, m.updated, m.team_id, m.slug, m.license, m.client_side, m.server_side\n FROM mods m\n WHERE id = $1\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": "follows",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "icon_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "body_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "updated",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "team_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "slug",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "license",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "client_side",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "server_side",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"fa911efc808e726c13659d3ce6baf61dc562e6f1e73fd65537a4ab1dad17120e": {
|
||||
"query": "\n DELETE FROM downloads\n WHERE downloads.version_id = $1\n ",
|
||||
"describe": {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use super::ids::*;
|
||||
use crate::database::models::User;
|
||||
use crate::models::teams::Permissions;
|
||||
|
||||
pub struct TeamBuilder {
|
||||
@ -78,6 +79,17 @@ pub struct TeamMember {
|
||||
pub accepted: bool,
|
||||
}
|
||||
|
||||
/// A member of a team
|
||||
pub struct QueryTeamMember {
|
||||
pub id: TeamMemberId,
|
||||
pub team_id: TeamId,
|
||||
/// The user associated with the member
|
||||
pub user: User,
|
||||
pub role: String,
|
||||
pub permissions: Permissions,
|
||||
pub accepted: bool,
|
||||
}
|
||||
|
||||
impl TeamMember {
|
||||
/// Lists the members of a team
|
||||
pub async fn get_from_team<'a, 'b, E>(
|
||||
@ -127,6 +139,68 @@ impl TeamMember {
|
||||
Ok(team_members)
|
||||
}
|
||||
|
||||
// Lists the full members of a team
|
||||
pub async fn get_from_team_full<'a, 'b, E>(
|
||||
id: TeamId,
|
||||
executor: E,
|
||||
) -> Result<Vec<QueryTeamMember>, super::DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let team_members = sqlx::query!(
|
||||
"
|
||||
SELECT tm.id id, tm.role member_role, tm.permissions permissions, tm.accepted accepted,
|
||||
u.id user_id, u.github_id github_id, u.name user_name, u.email email,
|
||||
u.avatar_url avatar_url, u.username username, u.bio bio,
|
||||
u.created created, u.role user_role
|
||||
FROM team_members tm
|
||||
INNER JOIN users u ON u.id = tm.user_id
|
||||
WHERE tm.team_id = $1
|
||||
",
|
||||
id as TeamId,
|
||||
)
|
||||
.fetch_many(executor)
|
||||
.try_filter_map(|e| async {
|
||||
if let Some(m) = e.right() {
|
||||
let permissions = Permissions::from_bits(m.permissions as u64);
|
||||
if let Some(perms) = permissions {
|
||||
Ok(Some(Ok(QueryTeamMember {
|
||||
id: TeamMemberId(m.id),
|
||||
team_id: id,
|
||||
role: m.member_role,
|
||||
permissions: perms,
|
||||
accepted: m.accepted,
|
||||
user: User {
|
||||
id: UserId(m.id),
|
||||
github_id: m.github_id,
|
||||
name: m.user_name,
|
||||
email: m.email,
|
||||
avatar_url: m.avatar_url,
|
||||
username: m.username,
|
||||
bio: m.bio,
|
||||
created: m.created,
|
||||
role: m.user_role,
|
||||
},
|
||||
})))
|
||||
} else {
|
||||
Ok(Some(Err(super::DatabaseError::BitflagError)))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
.try_collect::<Vec<Result<QueryTeamMember, super::DatabaseError>>>()
|
||||
.await?;
|
||||
|
||||
let team_members = team_members
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<QueryTeamMember>, super::DatabaseError>>()?;
|
||||
|
||||
Ok(team_members)
|
||||
}
|
||||
|
||||
/// Lists the team members for a user. Does not list pending requests.
|
||||
pub async fn get_from_user_public<'a, 'b, E>(
|
||||
id: UserId,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use super::ids::Base62Id;
|
||||
use crate::models::users::UserId;
|
||||
use crate::models::users::User;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The ID of a team
|
||||
@ -47,8 +47,8 @@ impl Default for Permissions {
|
||||
pub struct TeamMember {
|
||||
/// The ID of the team this team member is a member of
|
||||
pub team_id: TeamId,
|
||||
/// The ID of the user associated with the member
|
||||
pub user_id: UserId,
|
||||
/// The user associated with the member
|
||||
pub user: User,
|
||||
/// The role of the user in the team
|
||||
pub role: String,
|
||||
/// A bitset containing the user's permissions in this team
|
||||
|
||||
@ -8,7 +8,7 @@ pub struct UserId(pub u64);
|
||||
|
||||
pub const DELETED_USER: UserId = UserId(127155982985829);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct User {
|
||||
pub id: UserId,
|
||||
pub github_id: Option<u64>,
|
||||
@ -21,7 +21,7 @@ pub struct User {
|
||||
pub role: Role,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Role {
|
||||
Developer,
|
||||
|
||||
@ -54,6 +54,7 @@ pub fn projects_config(cfg: &mut web::ServiceConfig) {
|
||||
.service(projects::project_icon_edit)
|
||||
.service(projects::project_follow)
|
||||
.service(projects::project_unfollow)
|
||||
.service(teams::team_members_get_project)
|
||||
.service(web::scope("{project_id}").service(versions::version_list)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ pub struct CategoryData {
|
||||
// searching category list
|
||||
#[get("category")]
|
||||
pub async fn category_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
let results = Category::list(&**pool)
|
||||
let mut results = Category::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| CategoryData {
|
||||
@ -51,6 +51,8 @@ pub async fn category_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiE
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
results.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
|
||||
@ -115,7 +117,7 @@ pub struct LoaderData {
|
||||
|
||||
#[get("loader")]
|
||||
pub async fn loader_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
let results = Loader::list(&**pool)
|
||||
let mut results = Loader::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| LoaderData {
|
||||
@ -124,6 +126,9 @@ pub async fn loader_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiErr
|
||||
supported_project_types: x.supported_project_types,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
results.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::database::models::notification_item::{NotificationActionBuilder, NotificationBuilder};
|
||||
use crate::database::models::team_item::QueryTeamMember;
|
||||
use crate::database::models::TeamMember;
|
||||
use crate::models::ids::ProjectId;
|
||||
use crate::models::teams::{Permissions, TeamId};
|
||||
@ -9,6 +10,66 @@ use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[get("{id}/members")]
|
||||
pub async fn team_members_get_project(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
let project_data =
|
||||
crate::database::models::Project::get_from_slug_or_project_id(string, &**pool).await?;
|
||||
|
||||
if let Some(project) = project_data {
|
||||
let members_data = TeamMember::get_from_team_full(project.team_id, &**pool).await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
if let Some(user) = current_user {
|
||||
let team_member =
|
||||
TeamMember::get_from_user_id(project.team_id, user.id.into(), &**pool)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
|
||||
if team_member.is_some() {
|
||||
let team_members: Vec<crate::models::teams::TeamMember> = members_data
|
||||
.into_iter()
|
||||
.map(|data| convert_team_member(data, false))
|
||||
.collect();
|
||||
|
||||
return Ok(HttpResponse::Ok().json(team_members));
|
||||
}
|
||||
}
|
||||
|
||||
let team_members: Vec<crate::models::teams::TeamMember> = members_data
|
||||
.into_iter()
|
||||
.filter(|x| x.accepted)
|
||||
.map(|data| convert_team_member(data, true))
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(team_members))
|
||||
} else {
|
||||
Ok(HttpResponse::NotFound().body(""))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_team_member(
|
||||
data: QueryTeamMember,
|
||||
override_permissions: bool,
|
||||
) -> crate::models::teams::TeamMember {
|
||||
crate::models::teams::TeamMember {
|
||||
team_id: data.team_id.into(),
|
||||
user: super::users::convert_user(data.user),
|
||||
role: data.role,
|
||||
permissions: if override_permissions {
|
||||
None
|
||||
} else {
|
||||
Some(data.permissions)
|
||||
},
|
||||
accepted: data.accepted,
|
||||
}
|
||||
}
|
||||
|
||||
#[get("{id}/members")]
|
||||
pub async fn team_members_get(
|
||||
req: HttpRequest,
|
||||
@ -16,7 +77,7 @@ pub async fn team_members_get(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
let members_data = TeamMember::get_from_team(id.into(), &**pool).await?;
|
||||
let members_data = TeamMember::get_from_team_full(id.into(), &**pool).await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
@ -28,32 +89,18 @@ pub async fn team_members_get(
|
||||
if team_member.is_some() {
|
||||
let team_members: Vec<crate::models::teams::TeamMember> = members_data
|
||||
.into_iter()
|
||||
.map(|data| crate::models::teams::TeamMember {
|
||||
team_id: id,
|
||||
user_id: data.user_id.into(),
|
||||
role: data.role,
|
||||
permissions: Some(data.permissions),
|
||||
accepted: data.accepted,
|
||||
})
|
||||
.map(|data| convert_team_member(data, false))
|
||||
.collect();
|
||||
|
||||
return Ok(HttpResponse::Ok().json(team_members));
|
||||
}
|
||||
}
|
||||
|
||||
let mut team_members: Vec<crate::models::teams::TeamMember> = Vec::new();
|
||||
|
||||
for team_member in members_data {
|
||||
if team_member.accepted {
|
||||
team_members.push(crate::models::teams::TeamMember {
|
||||
team_id: id,
|
||||
user_id: team_member.user_id.into(),
|
||||
role: team_member.role,
|
||||
permissions: None,
|
||||
accepted: team_member.accepted,
|
||||
})
|
||||
}
|
||||
}
|
||||
let team_members: Vec<crate::models::teams::TeamMember> = members_data
|
||||
.into_iter()
|
||||
.filter(|x| x.accepted)
|
||||
.map(|data| convert_team_member(data, true))
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(team_members))
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::database::models::User;
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models::ids::ProjectId;
|
||||
use crate::models::notifications::Notification;
|
||||
use crate::models::projects::ProjectStatus;
|
||||
use crate::models::projects::{Project, ProjectStatus};
|
||||
use crate::models::users::{Role, UserId};
|
||||
use crate::routes::notifications::convert_notification;
|
||||
use crate::routes::ApiError;
|
||||
@ -75,7 +74,7 @@ pub async fn user_get(
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_user(data: crate::database::models::user_item::User) -> crate::models::users::User {
|
||||
pub fn convert_user(data: crate::database::models::user_item::User) -> crate::models::users::User {
|
||||
crate::models::users::User {
|
||||
id: data.id.into(),
|
||||
github_id: data.github_id.map(|i| i as u64),
|
||||
@ -114,10 +113,11 @@ pub async fn projects_list(
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool).await?
|
||||
};
|
||||
|
||||
let response = project_data
|
||||
let response = crate::database::Project::get_many_full(project_data, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|v| v.into())
|
||||
.collect::<Vec<crate::models::ids::ProjectId>>();
|
||||
.map(super::projects::convert_project)
|
||||
.collect::<Vec<Project>>();
|
||||
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
} else {
|
||||
@ -433,7 +433,7 @@ pub async fn user_follows(
|
||||
|
||||
use futures::TryStreamExt;
|
||||
|
||||
let projects: Vec<ProjectId> = sqlx::query!(
|
||||
let project_ids = sqlx::query!(
|
||||
"
|
||||
SELECT mf.mod_id FROM mod_follows mf
|
||||
WHERE mf.follower_id = $1
|
||||
@ -441,10 +441,19 @@ pub async fn user_follows(
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.fetch_many(&**pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| ProjectId(m.mod_id as u64))) })
|
||||
.try_collect::<Vec<ProjectId>>()
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right()
|
||||
.map(|m| crate::database::models::ProjectId(m.mod_id)))
|
||||
})
|
||||
.try_collect::<Vec<crate::database::models::ProjectId>>()
|
||||
.await?;
|
||||
|
||||
let projects = crate::database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(super::projects::convert_project)
|
||||
.collect::<Vec<Project>>();
|
||||
|
||||
Ok(HttpResponse::Ok().json(projects))
|
||||
} else {
|
||||
Ok(HttpResponse::NotFound().body(""))
|
||||
|
||||
@ -4,6 +4,7 @@ mod moderation;
|
||||
mod mods;
|
||||
mod reports;
|
||||
mod tags;
|
||||
mod teams;
|
||||
mod users;
|
||||
mod versions;
|
||||
|
||||
@ -31,7 +32,7 @@ pub fn tags_config(cfg: &mut web::ServiceConfig) {
|
||||
.service(tags::loader_list)
|
||||
.service(tags::loader_create)
|
||||
.service(super::tags::loader_delete)
|
||||
.service(super::tags::game_version_list)
|
||||
.service(tags::game_version_list)
|
||||
.service(super::tags::game_version_create)
|
||||
.service(super::tags::game_version_delete)
|
||||
.service(super::tags::license_create)
|
||||
@ -93,14 +94,14 @@ pub fn users_config(cfg: &mut web::ServiceConfig) {
|
||||
.service(super::users::user_edit)
|
||||
.service(super::users::user_icon_edit)
|
||||
.service(super::users::user_notifications)
|
||||
.service(super::users::user_follows),
|
||||
.service(users::user_follows),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn teams_config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("team")
|
||||
.service(super::teams::team_members_get)
|
||||
.service(teams::team_members_get)
|
||||
.service(super::teams::edit_team_member)
|
||||
.service(super::teams::add_team_member)
|
||||
.service(super::teams::join_team)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::auth::check_is_admin_from_headers;
|
||||
use crate::database::models::categories::{Category, Loader, ProjectType};
|
||||
use crate::database::models::categories::{Category, GameVersion, Loader, ProjectType};
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{get, put, web};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
@ -79,3 +79,32 @@ pub async fn loader_create(
|
||||
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct GameVersionQueryData {
|
||||
#[serde(rename = "type")]
|
||||
type_: Option<String>,
|
||||
major: Option<bool>,
|
||||
}
|
||||
|
||||
#[get("game_version")]
|
||||
pub async fn game_version_list(
|
||||
pool: web::Data<PgPool>,
|
||||
query: web::Query<GameVersionQueryData>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if query.type_.is_some() || query.major.is_some() {
|
||||
let results = GameVersion::list_filter(query.type_.as_deref(), query.major, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.version)
|
||||
.collect::<Vec<String>>();
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
} else {
|
||||
let results = GameVersion::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.version)
|
||||
.collect::<Vec<String>>();
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
}
|
||||
|
||||
76
src/routes/v1/teams.rs
Normal file
76
src/routes/v1/teams.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::models::teams::{Permissions, TeamId};
|
||||
use crate::models::users::UserId;
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{get, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
/// A member of a team
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct TeamMember {
|
||||
/// The ID of the team this team member is a member of
|
||||
pub team_id: TeamId,
|
||||
/// The ID of the user associated with the member
|
||||
pub user_id: UserId,
|
||||
/// The role of the user in the team
|
||||
pub role: String,
|
||||
/// A bitset containing the user's permissions in this team
|
||||
pub permissions: Option<Permissions>,
|
||||
/// Whether the user has joined the team or is just invited to it
|
||||
pub accepted: bool,
|
||||
}
|
||||
|
||||
#[get("{id}/members")]
|
||||
pub async fn team_members_get(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(TeamId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
let members_data =
|
||||
crate::database::models::TeamMember::get_from_team(id.into(), &**pool).await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
if let Some(user) = current_user {
|
||||
let team_member = crate::database::models::TeamMember::get_from_user_id(
|
||||
id.into(),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
|
||||
if team_member.is_some() {
|
||||
let team_members: Vec<TeamMember> = members_data
|
||||
.into_iter()
|
||||
.map(|data| TeamMember {
|
||||
team_id: id,
|
||||
user_id: data.user_id.into(),
|
||||
role: data.role,
|
||||
permissions: Some(data.permissions),
|
||||
accepted: data.accepted,
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(HttpResponse::Ok().json(team_members));
|
||||
}
|
||||
}
|
||||
|
||||
let mut team_members: Vec<TeamMember> = Vec::new();
|
||||
|
||||
for team_member in members_data {
|
||||
if team_member.accepted {
|
||||
team_members.push(TeamMember {
|
||||
team_id: id,
|
||||
user_id: team_member.user_id.into(),
|
||||
role: team_member.role,
|
||||
permissions: None,
|
||||
accepted: team_member.accepted,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().json(team_members))
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::database::models::User;
|
||||
use crate::models::ids::UserId;
|
||||
use crate::models::projects::ProjectStatus;
|
||||
use crate::models::projects::{ProjectId, ProjectStatus};
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::web;
|
||||
use actix_web::{get, HttpRequest, HttpResponse};
|
||||
@ -42,3 +42,41 @@ pub async fn mods_list(
|
||||
Ok(HttpResponse::NotFound().body(""))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("{id}/follows")]
|
||||
pub async fn user_follows(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
if !user.role.is_mod() && user.id != id.into() {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You do not have permission to see the projects this user follows!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
use futures::TryStreamExt;
|
||||
|
||||
let projects: Vec<ProjectId> = sqlx::query!(
|
||||
"
|
||||
SELECT mf.mod_id FROM mod_follows mf
|
||||
WHERE mf.follower_id = $1
|
||||
",
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.fetch_many(&**pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| ProjectId(m.mod_id as u64))) })
|
||||
.try_collect::<Vec<ProjectId>>()
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(projects))
|
||||
} else {
|
||||
Ok(HttpResponse::NotFound().body(""))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,314 +1,165 @@
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use futures::TryStreamExt;
|
||||
use log::info;
|
||||
|
||||
use super::IndexingError;
|
||||
use crate::models::projects::SideType;
|
||||
use crate::database::models::ProjectId;
|
||||
use crate::models::projects::ProjectStatus;
|
||||
use crate::search::UploadSearchProject;
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::borrow::Cow;
|
||||
|
||||
// TODO: only loaders for recent versions? For projects that have moved from forge to fabric
|
||||
pub async fn index_local(pool: PgPool) -> Result<Vec<UploadSearchProject>, IndexingError> {
|
||||
info!("Indexing local projects!");
|
||||
|
||||
let mut docs_to_add: Vec<UploadSearchProject> = vec![];
|
||||
|
||||
let mut projects = sqlx::query!(
|
||||
"
|
||||
SELECT m.id, m.title, m.description, m.downloads, m.follows, m.icon_url, m.body_url, m.published, m.updated, m.team_id, m.status, m.slug, m.license, m.client_side, m.server_side FROM mods m
|
||||
"
|
||||
).fetch(&pool);
|
||||
|
||||
while let Some(result) = projects.next().await {
|
||||
if let Ok(project_data) = result {
|
||||
let status = crate::models::projects::ProjectStatus::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT status FROM statuses
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.status,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
.status,
|
||||
);
|
||||
|
||||
if !status.is_searchable() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let versions = sqlx::query!(
|
||||
"
|
||||
SELECT DISTINCT gv.version, gv.created FROM versions
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id=versions.id
|
||||
INNER JOIN game_versions gv ON gvv.game_version_id=gv.id
|
||||
WHERE versions.mod_id = $1
|
||||
ORDER BY gv.created ASC
|
||||
",
|
||||
project_data.id
|
||||
)
|
||||
Ok(
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,
|
||||
m.icon_url icon_url, m.published published,
|
||||
m.updated updated,
|
||||
m.team_id team_id, m.license license, m.slug slug,
|
||||
s.status status_name, cs.name client_side_type, ss.name server_side_type, l.short short, pt.name project_type_name, u.username username,
|
||||
STRING_AGG(DISTINCT c.category, ',') categories, STRING_AGG(DISTINCT lo.loader, ',') loaders, STRING_AGG(DISTINCT gv.version, ',') versions
|
||||
FROM mods m
|
||||
LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id
|
||||
LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id
|
||||
LEFT OUTER JOIN versions v ON v.mod_id = m.id
|
||||
INNER JOIN statuses s ON s.id = m.status
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id
|
||||
INNER JOIN game_versions gv ON gvv.game_version_id = gv.id
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id
|
||||
INNER JOIN loaders lo ON lo.id = lv.loader_id
|
||||
INNER JOIN project_types pt ON pt.id = m.project_type
|
||||
INNER JOIN side_types cs ON m.client_side = cs.id
|
||||
INNER JOIN side_types ss ON m.server_side = ss.id
|
||||
INNER JOIN licenses l ON m.license = l.id
|
||||
INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $2
|
||||
INNER JOIN users u ON tm.user_id = u.id
|
||||
WHERE s.status = $1
|
||||
GROUP BY m.id, s.id, cs.id, ss.id, l.id, pt.id, u.id;
|
||||
",
|
||||
ProjectStatus::Approved.as_str(),
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
)
|
||||
.fetch_many(&pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.version)) })
|
||||
.try_collect::<Vec<String>>()
|
||||
.await?;
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|m| {
|
||||
let mut categories = m.categories.unwrap_or_default().split(',').map(|x| x.to_string()).collect::<Vec<String>>();
|
||||
categories.append(&mut m.loaders.unwrap_or_default().split(',').map(|x| x.to_string()).collect::<Vec<String>>());
|
||||
|
||||
let loaders = sqlx::query!(
|
||||
"
|
||||
SELECT DISTINCT loaders.loader FROM versions
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = versions.id
|
||||
INNER JOIN loaders ON loaders.id = lv.loader_id
|
||||
WHERE versions.mod_id = $1
|
||||
",
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.loader))) })
|
||||
.try_collect::<Vec<Cow<str>>>()
|
||||
.await?;
|
||||
let versions : Vec<String> = m.versions.unwrap_or_default().split(',').map(|x| x.to_string()).collect::<Vec<String>>();
|
||||
|
||||
let mut categories = sqlx::query!(
|
||||
"
|
||||
SELECT c.category
|
||||
FROM mods_categories mc
|
||||
INNER JOIN categories c ON mc.joining_category_id=c.id
|
||||
WHERE mc.joining_mod_id = $1
|
||||
",
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.category))) })
|
||||
.try_collect::<Vec<Cow<str>>>()
|
||||
.await?;
|
||||
let project_id : crate::models::projects::ProjectId = ProjectId(m.id).into();
|
||||
|
||||
categories.extend(loaders);
|
||||
|
||||
let user = sqlx::query!(
|
||||
"
|
||||
SELECT u.id, u.username FROM users u
|
||||
INNER JOIN team_members tm ON tm.user_id = u.id
|
||||
WHERE tm.team_id = $2 AND tm.role = $1
|
||||
",
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
project_data.team_id,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?;
|
||||
|
||||
let mut icon_url = "".to_string();
|
||||
|
||||
if let Some(url) = project_data.icon_url {
|
||||
icon_url = url;
|
||||
}
|
||||
|
||||
let project_id = crate::models::ids::ProjectId(project_data.id as u64);
|
||||
|
||||
// TODO: is this correct? This just gets the latest version of
|
||||
// minecraft that this project has a version that supports; it doesn't
|
||||
// take betas or other info into account.
|
||||
let latest_version = versions
|
||||
.last()
|
||||
.cloned()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or_else(|| Cow::Borrowed(""));
|
||||
|
||||
let client_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.client_side,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
.name,
|
||||
);
|
||||
|
||||
let server_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.server_side,
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await?
|
||||
.name,
|
||||
);
|
||||
|
||||
let license = crate::database::models::categories::License::get(
|
||||
crate::database::models::LicenseId(project_data.license),
|
||||
&pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
docs_to_add.push(UploadSearchProject {
|
||||
project_id: format!("local-{}", project_id),
|
||||
title: project_data.title,
|
||||
description: project_data.description,
|
||||
categories,
|
||||
versions,
|
||||
follows: project_data.follows,
|
||||
downloads: project_data.downloads,
|
||||
icon_url,
|
||||
author: user.username,
|
||||
date_created: project_data.published,
|
||||
created_timestamp: project_data.published.timestamp(),
|
||||
date_modified: project_data.updated,
|
||||
modified_timestamp: project_data.updated.timestamp(),
|
||||
latest_version,
|
||||
license: license.short,
|
||||
client_side: client_side.to_string(),
|
||||
server_side: server_side.to_string(),
|
||||
host: Cow::Borrowed("modrinth"),
|
||||
slug: project_data.slug,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(docs_to_add)
|
||||
UploadSearchProject {
|
||||
project_id: format!("{}", project_id),
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
categories,
|
||||
follows: m.follows,
|
||||
downloads: m.downloads,
|
||||
icon_url: m.icon_url.unwrap_or_default(),
|
||||
author: m.username,
|
||||
date_created: m.published,
|
||||
created_timestamp: m.published.timestamp(),
|
||||
date_modified: m.updated,
|
||||
modified_timestamp: m.updated.timestamp(),
|
||||
latest_version: versions.last().cloned().unwrap_or_else(|| "None".to_string()),
|
||||
versions,
|
||||
license: m.short,
|
||||
client_side: m.client_side_type,
|
||||
server_side: m.server_side_type,
|
||||
slug: m.slug,
|
||||
project_type: m.project_type_name,
|
||||
}
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<UploadSearchProject>>()
|
||||
.await?
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn query_one(
|
||||
id: crate::database::models::ProjectId,
|
||||
id: ProjectId,
|
||||
exec: &mut sqlx::PgConnection,
|
||||
) -> Result<UploadSearchProject, IndexingError> {
|
||||
let project_data = sqlx::query!(
|
||||
"
|
||||
SELECT m.id, m.title, m.description, m.downloads, m.follows, m.icon_url, m.body_url, m.published, m.updated, m.team_id, m.slug, m.license, m.client_side, m.server_side
|
||||
FROM mods m
|
||||
WHERE id = $1
|
||||
",
|
||||
id.0,
|
||||
).fetch_one(&mut *exec).await?;
|
||||
|
||||
let versions = sqlx::query!(
|
||||
"
|
||||
SELECT DISTINCT gv.version, gv.created FROM versions
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id=versions.id
|
||||
INNER JOIN game_versions gv ON gvv.game_version_id=gv.id
|
||||
WHERE versions.mod_id = $1
|
||||
ORDER BY gv.created ASC
|
||||
",
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&mut *exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.version)) })
|
||||
.try_collect::<Vec<String>>()
|
||||
.await?;
|
||||
|
||||
let loaders = sqlx::query!(
|
||||
"
|
||||
SELECT DISTINCT loaders.loader FROM versions
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = versions.id
|
||||
INNER JOIN loaders ON loaders.id = lv.loader_id
|
||||
WHERE versions.mod_id = $1
|
||||
",
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&mut *exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.loader))) })
|
||||
.try_collect::<Vec<Cow<str>>>()
|
||||
.await?;
|
||||
|
||||
let mut categories = sqlx::query!(
|
||||
"
|
||||
SELECT c.category
|
||||
FROM mods_categories mc
|
||||
INNER JOIN categories c ON mc.joining_category_id=c.id
|
||||
WHERE mc.joining_mod_id = $1
|
||||
",
|
||||
project_data.id
|
||||
)
|
||||
.fetch_many(&mut *exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| Cow::Owned(c.category))) })
|
||||
.try_collect::<Vec<Cow<str>>>()
|
||||
.await?;
|
||||
|
||||
categories.extend(loaders);
|
||||
|
||||
let user = sqlx::query!(
|
||||
"
|
||||
SELECT u.id, u.username FROM users u
|
||||
INNER JOIN team_members tm ON tm.user_id = u.id
|
||||
WHERE tm.team_id = $2 AND tm.role = $1
|
||||
",
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
project_data.team_id,
|
||||
)
|
||||
.fetch_one(&mut *exec)
|
||||
.await?;
|
||||
|
||||
let mut icon_url = "".to_string();
|
||||
|
||||
if let Some(url) = project_data.icon_url {
|
||||
icon_url = url;
|
||||
}
|
||||
|
||||
let project_id = crate::models::ids::ProjectId(project_data.id as u64);
|
||||
|
||||
// TODO: is this correct? This just gets the latest version of
|
||||
// minecraft that this project has a version that supports; it doesn't
|
||||
// take betas or other info into account.
|
||||
let latest_version = versions
|
||||
.last()
|
||||
.cloned()
|
||||
.map(Cow::Owned)
|
||||
.unwrap_or_else(|| Cow::Borrowed(""));
|
||||
|
||||
let client_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
let m = sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
SELECT m.id id, m.project_type project_type, m.title title, m.description description, m.downloads downloads, m.follows follows,
|
||||
m.icon_url icon_url, m.published published,
|
||||
m.updated updated,
|
||||
m.team_id team_id, m.license license, m.slug slug,
|
||||
s.status status_name, cs.name client_side_type, ss.name server_side_type, l.short short, pt.name project_type_name, u.username username,
|
||||
STRING_AGG(DISTINCT c.category, ',') categories, STRING_AGG(DISTINCT lo.loader, ',') loaders, STRING_AGG(DISTINCT gv.version, ',') versions
|
||||
FROM mods m
|
||||
LEFT OUTER JOIN mods_categories mc ON joining_mod_id = m.id
|
||||
LEFT OUTER JOIN categories c ON mc.joining_category_id = c.id
|
||||
LEFT OUTER JOIN versions v ON v.mod_id = m.id
|
||||
INNER JOIN statuses s ON s.id = m.status
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id
|
||||
INNER JOIN game_versions gv ON gvv.game_version_id = gv.id
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id
|
||||
INNER JOIN loaders lo ON lo.id = lv.loader_id
|
||||
INNER JOIN project_types pt ON pt.id = m.project_type
|
||||
INNER JOIN side_types cs ON m.client_side = cs.id
|
||||
INNER JOIN side_types ss ON m.server_side = ss.id
|
||||
INNER JOIN licenses l ON m.license = l.id
|
||||
INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.role = $2
|
||||
INNER JOIN users u ON tm.user_id = u.id
|
||||
WHERE m.id = $1
|
||||
GROUP BY m.id, s.id, cs.id, ss.id, l.id, pt.id, u.id;
|
||||
",
|
||||
project_data.client_side,
|
||||
id as ProjectId,
|
||||
crate::models::teams::OWNER_ROLE,
|
||||
)
|
||||
.fetch_one(&mut *exec)
|
||||
.await?
|
||||
.name,
|
||||
.fetch_one(exec)
|
||||
.await?;
|
||||
|
||||
let mut categories = m
|
||||
.categories
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
categories.append(
|
||||
&mut m
|
||||
.loaders
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
|
||||
let server_side = SideType::from_str(
|
||||
&sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
WHERE id = $1
|
||||
",
|
||||
project_data.server_side,
|
||||
)
|
||||
.fetch_one(&mut *exec)
|
||||
.await?
|
||||
.name,
|
||||
);
|
||||
let versions: Vec<String> = m
|
||||
.versions
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let license = crate::database::models::categories::License::get(
|
||||
crate::database::models::LicenseId(project_data.license),
|
||||
&mut *exec,
|
||||
)
|
||||
.await?;
|
||||
let project_id: crate::models::projects::ProjectId = ProjectId(m.id).into();
|
||||
|
||||
Ok(UploadSearchProject {
|
||||
project_id: format!("local-{}", project_id),
|
||||
title: project_data.title,
|
||||
description: project_data.description,
|
||||
project_id: format!("{}", project_id),
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
categories,
|
||||
follows: m.follows,
|
||||
downloads: m.downloads,
|
||||
icon_url: m.icon_url.unwrap_or_default(),
|
||||
author: m.username,
|
||||
date_created: m.published,
|
||||
created_timestamp: m.published.timestamp(),
|
||||
date_modified: m.updated,
|
||||
modified_timestamp: m.updated.timestamp(),
|
||||
latest_version: versions
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "None".to_string()),
|
||||
versions,
|
||||
follows: project_data.follows,
|
||||
downloads: project_data.downloads,
|
||||
icon_url,
|
||||
author: user.username,
|
||||
date_created: project_data.published,
|
||||
created_timestamp: project_data.published.timestamp(),
|
||||
date_modified: project_data.updated,
|
||||
modified_timestamp: project_data.updated.timestamp(),
|
||||
latest_version,
|
||||
license: license.short,
|
||||
client_side: client_side.to_string(),
|
||||
server_side: server_side.to_string(),
|
||||
host: Cow::Borrowed("modrinth"),
|
||||
slug: project_data.slug,
|
||||
license: m.short,
|
||||
client_side: m.client_side_type,
|
||||
server_side: m.server_side_type,
|
||||
slug: m.slug,
|
||||
project_type: m.project_type_name,
|
||||
})
|
||||
}
|
||||
|
||||
@ -264,6 +264,7 @@ fn default_rules() -> VecDeque<String> {
|
||||
fn default_settings() -> Settings {
|
||||
let displayed_attributes = vec![
|
||||
"project_id".to_string(),
|
||||
"project_type".to_string(),
|
||||
"slug".to_string(),
|
||||
"author".to_string(),
|
||||
"title".to_string(),
|
||||
|
||||
@ -62,16 +62,17 @@ pub struct SearchConfig {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct UploadSearchProject {
|
||||
pub project_id: String,
|
||||
pub project_type: String,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub categories: Vec<Cow<'static, str>>,
|
||||
pub categories: Vec<String>,
|
||||
pub versions: Vec<String>,
|
||||
pub follows: i32,
|
||||
pub downloads: i32,
|
||||
pub icon_url: String,
|
||||
pub latest_version: Cow<'static, str>,
|
||||
pub latest_version: String,
|
||||
pub license: String,
|
||||
pub client_side: String,
|
||||
pub server_side: String,
|
||||
@ -84,8 +85,6 @@ pub struct UploadSearchProject {
|
||||
pub date_modified: DateTime<Utc>,
|
||||
/// Unix timestamp of the last major modification
|
||||
pub modified_timestamp: i64,
|
||||
|
||||
pub host: Cow<'static, str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -99,6 +98,7 @@ pub struct SearchResults {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ResultSearchProject {
|
||||
pub project_id: String,
|
||||
pub project_type: String,
|
||||
pub slug: Option<String>,
|
||||
pub author: String,
|
||||
pub title: String,
|
||||
|
||||
@ -30,7 +30,11 @@ impl super::Validator for FabricValidator {
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
archive.by_name("fabric.mod.json")?;
|
||||
archive.by_name("fabric.mod.json").map_err(|_| {
|
||||
ValidationError::InvalidInputError(
|
||||
"No fabric.mod.json present for Fabric file.".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !archive
|
||||
.file_names()
|
||||
|
||||
@ -30,7 +30,9 @@ impl super::Validator for ForgeValidator {
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
archive.by_name("META-INF/mods.toml")?;
|
||||
archive.by_name("META-INF/mods.toml").map_err(|_| {
|
||||
ValidationError::InvalidInputError("No mods.toml present for Forge file.".to_string())
|
||||
})?;
|
||||
|
||||
if !archive.file_names().any(|name| name.ends_with(".class")) {
|
||||
return Ok(ValidationResult::Warning(
|
||||
@ -71,7 +73,9 @@ impl super::Validator for LegacyForgeValidator {
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
archive.by_name("mcmod.info")?;
|
||||
archive.by_name("mcmod.info").map_err(|_| {
|
||||
ValidationError::InvalidInputError("No mcmod.info present for Forge file.".to_string())
|
||||
})?;
|
||||
|
||||
if !archive.file_names().any(|name| name.ends_with(".class")) {
|
||||
return Ok(ValidationResult::Warning(
|
||||
|
||||
@ -78,7 +78,9 @@ impl super::Validator for PackValidator {
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
let mut file = archive.by_name("index.json")?;
|
||||
let mut file = archive.by_name("index.json").map_err(|_| {
|
||||
ValidationError::InvalidInputError("Pack manifest is missing.".to_string())
|
||||
})?;
|
||||
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user