Add API routes to request multiple of an item (#70)
* Change header name * Add default bio value * Remove default * Make name null * Run prepare * Add new API Routes for requesting multiple of an item * Run formatter * Simplify get mods query * Run prepare * Refactor to use one query for most routes, change version create route to have mod_id in data * More fixes
This commit is contained in:
parent
68ee2bdcdc
commit
2719ae5df2
256
sqlx-data.json
256
sqlx-data.json
@ -81,6 +81,26 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"1220d15a56dbf823eaa452fbafa17442ab0568bc81a31fa38e16e3df3278e5f9": {
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"1524c0462be70077736ac70fcd037fbf75651456b692e2ce40fa2e3fc8123984": {
|
||||
"query": "\n SELECT hashes.algorithm, hashes.hash FROM hashes\n WHERE hashes.file_id = $1\n ",
|
||||
"describe": {
|
||||
@ -123,6 +143,74 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"17e6d30c3693e9bd9f772f3dc4e2eafe75fdeecfdcf2746eac641f77ced6b8a8": {
|
||||
"query": "\n SELECT u.id, u.github_id, u.name, u.email,\n u.avatar_url, u.username, u.bio,\n u.created, u.role FROM users u\n WHERE u.id IN (SELECT * FROM UNNEST($1::bigint[]))\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "github_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "avatar_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "username",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "bio",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "created",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "role",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"1b74bdb59773ffd2a78a56e4d920bb83c322e180e6174c741d4bb722c353de43": {
|
||||
"query": "\n INSERT INTO loaders (loader)\n VALUES ($1)\n RETURNING id\n ",
|
||||
"describe": {
|
||||
@ -203,6 +291,86 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"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 ",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "issues_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"275939f581a82197b45b0d56248926063f09ef86754498a720c5568cdb1f5ae0": {
|
||||
"query": "SELECT user_id FROM team_members WHERE team_id=$1",
|
||||
"describe": {
|
||||
@ -1183,6 +1351,74 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"d98f0713c6bd3463f851644c88a2cf91601a499494ef1e2ad92a49d6798f5b2f": {
|
||||
"query": "\n SELECT v.id, v.mod_id, v.author_id, v.name, v.version_number,\n v.changelog_url, v.date_published, v.downloads,\n v.release_channel\n FROM versions v\n WHERE v.id IN (SELECT * FROM UNNEST($1::bigint[]))\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "mod_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "author_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "version_number",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "changelog_url",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "date_published",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "downloads",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "release_channel",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"deb81673526789bca38d39e64303f61d2a63febfdfb68136e58517af9f7792bc": {
|
||||
"query": "\n SELECT category FROM mods_categories\n INNER JOIN categories ON joining_category_id = id\n WHERE joining_mod_id = $1\n ",
|
||||
"describe": {
|
||||
@ -1537,5 +1773,25 @@
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"fdb2a6ea649bb23c69af5c756d6137e216603708ffccd4e9162fb1c9765a56aa": {
|
||||
"query": "\n SELECT m.id FROM mods m\n INNER JOIN team_members tm ON tm.team_id = m.team_id\n WHERE tm.user_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,6 +143,46 @@ impl Mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_many<'a, E>(mod_ids: Vec<ModId>, exec: E) -> Result<Vec<Mod>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let mod_ids_parsed: Vec<i64> = mod_ids.into_iter().map(|x| x.0).collect();
|
||||
let mods = sqlx::query!(
|
||||
"
|
||||
SELECT id, title, description, downloads,
|
||||
icon_url, body_url, published,
|
||||
issues_url, source_url, wiki_url,
|
||||
team_id
|
||||
FROM mods
|
||||
WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
&mod_ids_parsed
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|m| Mod {
|
||||
id: ModId(m.id),
|
||||
team_id: TeamId(m.team_id),
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
downloads: m.downloads,
|
||||
body_url: m.body_url,
|
||||
icon_url: m.icon_url,
|
||||
published: m.published,
|
||||
issues_url: m.issues_url,
|
||||
source_url: m.source_url,
|
||||
wiki_url: m.wiki_url,
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<Mod>>()
|
||||
.await?;
|
||||
|
||||
Ok(mods)
|
||||
}
|
||||
|
||||
pub async fn remove_full<'a, 'b, E>(
|
||||
id: ModId,
|
||||
exec: E,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use super::ids::UserId;
|
||||
use super::ids::{ModId, UserId};
|
||||
|
||||
pub struct User {
|
||||
pub id: UserId,
|
||||
@ -112,4 +112,62 @@ impl User {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_many<'a, E>(user_ids: Vec<UserId>, exec: E) -> Result<Vec<User>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let user_ids_parsed: Vec<i64> = user_ids.into_iter().map(|x| x.0).collect();
|
||||
let users = sqlx::query!(
|
||||
"
|
||||
SELECT u.id, u.github_id, u.name, u.email,
|
||||
u.avatar_url, u.username, u.bio,
|
||||
u.created, u.role FROM users u
|
||||
WHERE u.id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
&user_ids_parsed
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|u| User {
|
||||
id: UserId(u.id),
|
||||
github_id: u.github_id,
|
||||
name: u.name,
|
||||
email: u.email,
|
||||
avatar_url: u.avatar_url,
|
||||
username: u.username,
|
||||
bio: u.bio,
|
||||
created: u.created,
|
||||
role: u.role,
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<User>>()
|
||||
.await?;
|
||||
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
pub async fn get_mods<'a, E>(user_id: UserId, exec: E) -> Result<Vec<ModId>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let mods = sqlx::query!(
|
||||
"
|
||||
SELECT m.id FROM mods m
|
||||
INNER JOIN team_members tm ON tm.team_id = m.team_id
|
||||
WHERE tm.user_id = $1
|
||||
",
|
||||
user_id as UserId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| ModId(m.id))) })
|
||||
.try_collect::<Vec<ModId>>()
|
||||
.await?;
|
||||
|
||||
Ok(mods)
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,6 +371,46 @@ impl Version {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_many<'a, E>(
|
||||
version_ids: Vec<VersionId>,
|
||||
exec: E,
|
||||
) -> Result<Vec<Version>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let version_ids_parsed: Vec<i64> = version_ids.into_iter().map(|x| x.0).collect();
|
||||
let versions = sqlx::query!(
|
||||
"
|
||||
SELECT v.id, v.mod_id, v.author_id, v.name, v.version_number,
|
||||
v.changelog_url, v.date_published, v.downloads,
|
||||
v.release_channel
|
||||
FROM versions v
|
||||
WHERE v.id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
&version_ids_parsed
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|v| Version {
|
||||
id: VersionId(v.id),
|
||||
mod_id: ModId(v.mod_id),
|
||||
author_id: UserId(v.author_id),
|
||||
name: v.name,
|
||||
version_number: v.version_number,
|
||||
changelog_url: v.changelog_url,
|
||||
date_published: v.date_published,
|
||||
downloads: v.downloads,
|
||||
release_channel: ChannelId(v.release_channel),
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<Version>>()
|
||||
.await?;
|
||||
|
||||
Ok(versions)
|
||||
}
|
||||
|
||||
pub async fn get_full<'a, 'b, E>(
|
||||
id: VersionId,
|
||||
executor: E,
|
||||
|
||||
@ -204,6 +204,7 @@ async fn main() -> std::io::Result<()> {
|
||||
.configure(routes::auth_config)
|
||||
.configure(routes::tags_config)
|
||||
.configure(routes::mods_config)
|
||||
.configure(routes::versions_config)
|
||||
.configure(routes::users_config),
|
||||
)
|
||||
.default_service(web::get().to(routes::not_found))
|
||||
|
||||
@ -13,6 +13,7 @@ pub use super::users::UserId;
|
||||
///
|
||||
/// This method panics if `n` is 0 or greater than 11, since a `u64`
|
||||
/// can only represent up to 11 character base62 strings
|
||||
#[allow(dead_code)]
|
||||
pub fn random_base62(n: usize) -> u64 {
|
||||
use rand::Rng;
|
||||
assert!(n > 0 && n <= 11);
|
||||
|
||||
@ -18,33 +18,36 @@ pub use self::not_found::not_found;
|
||||
|
||||
pub fn mods_config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(mods::mod_search);
|
||||
cfg.service(mods::mods_get);
|
||||
cfg.service(mod_creation::mod_create);
|
||||
|
||||
cfg.service(
|
||||
web::scope("mod")
|
||||
.service(mods::mod_get)
|
||||
.service(mods::mod_delete)
|
||||
.service(web::scope("{mod_id}").configure(versions_config)),
|
||||
.service(web::scope("{mod_id}").service(versions::version_list)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn versions_config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(versions::version_list)
|
||||
.service(version_creation::version_create)
|
||||
.service(
|
||||
web::scope("version")
|
||||
.service(versions::version_get)
|
||||
.service(versions::version_delete)
|
||||
.service(version_creation::upload_file_to_version),
|
||||
);
|
||||
cfg.service(versions::versions_get);
|
||||
cfg.service(
|
||||
web::scope("version")
|
||||
.service(versions::version_get)
|
||||
.service(version_creation::version_create)
|
||||
.service(versions::version_delete)
|
||||
.service(version_creation::upload_file_to_version),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn users_config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(users::user_auth_get);
|
||||
|
||||
cfg.service(users::users_get);
|
||||
cfg.service(
|
||||
web::scope("user")
|
||||
.service(users::user_get)
|
||||
.service(users::mods_list)
|
||||
.service(users::user_delete),
|
||||
);
|
||||
}
|
||||
@ -53,6 +56,8 @@ pub fn users_config(cfg: &mut web::ServiceConfig) {
|
||||
pub enum ApiError {
|
||||
#[error("Internal server error")]
|
||||
DatabaseError(#[from] crate::database::models::DatabaseError),
|
||||
#[error("Deserialization error: {0}")]
|
||||
JsonError(#[from] serde_json::Error),
|
||||
#[error("Authentication Error")]
|
||||
AuthenticationError,
|
||||
}
|
||||
@ -62,6 +67,7 @@ impl actix_web::ResponseError for ApiError {
|
||||
match self {
|
||||
ApiError::DatabaseError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::AuthenticationError => actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
ApiError::JsonError(..) => actix_web::http::StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +77,7 @@ impl actix_web::ResponseError for ApiError {
|
||||
error: match self {
|
||||
ApiError::DatabaseError(..) => "database_error",
|
||||
ApiError::AuthenticationError => "unauthorized",
|
||||
ApiError::JsonError(..) => "json_error",
|
||||
},
|
||||
description: &self.to_string(),
|
||||
},
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::models;
|
||||
use crate::models::mods::SearchRequest;
|
||||
use crate::search::{search_for_mod, SearchError};
|
||||
use actix_web::{delete, get, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[get("mod")]
|
||||
@ -15,6 +16,49 @@ pub async fn mod_search(
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ModIds {
|
||||
pub ids: String,
|
||||
}
|
||||
|
||||
// TODO: Make this return the full mod struct
|
||||
#[get("mods")]
|
||||
pub async fn mods_get(
|
||||
web::Query(ids): web::Query<ModIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mod_ids = serde_json::from_str::<Vec<models::ids::ModId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
|
||||
let mods_data = database::models::Mod::get_many(mod_ids, &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
let mods: Vec<models::mods::Mod> = mods_data
|
||||
.into_iter()
|
||||
.map(|m| 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,
|
||||
|
||||
downloads: m.downloads as u32,
|
||||
categories: vec![],
|
||||
versions: vec![],
|
||||
icon_url: m.icon_url,
|
||||
issues_url: m.issues_url,
|
||||
source_url: m.source_url,
|
||||
wiki_url: m.wiki_url,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(mods))
|
||||
}
|
||||
|
||||
#[get("{id}")]
|
||||
pub async fn mod_get(
|
||||
info: web::Path<(models::ids::ModId,)>,
|
||||
@ -48,7 +92,6 @@ pub async fn mod_get(
|
||||
Ok(HttpResponse::NotFound().body(""))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The mod remains in meilisearch's index until the index is deleted
|
||||
#[delete("{id}")]
|
||||
pub async fn mod_delete(
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
use crate::auth::{check_is_moderator_from_headers, get_user_from_headers};
|
||||
use crate::database::models::User;
|
||||
use crate::models::users::{Role, UserId};
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{delete, get, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
#[get("user")]
|
||||
@ -22,13 +24,50 @@ pub async fn user_auth_get(
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UserIds {
|
||||
pub ids: String,
|
||||
}
|
||||
|
||||
#[get("users")]
|
||||
pub async fn users_get(
|
||||
web::Query(ids): web::Query<UserIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_ids = serde_json::from_str::<Vec<UserId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
|
||||
let users_data = User::get_many(user_ids, &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
let users: Vec<crate::models::users::User> = users_data
|
||||
.into_iter()
|
||||
.map(|data| crate::models::users::User {
|
||||
id: data.id.into(),
|
||||
github_id: data.github_id.map(|i| i as u64),
|
||||
username: data.username,
|
||||
name: data.name,
|
||||
email: None,
|
||||
avatar_url: data.avatar_url,
|
||||
bio: data.bio,
|
||||
created: data.created,
|
||||
role: Role::from_string(&*data.role),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(users))
|
||||
}
|
||||
|
||||
#[get("{id}")]
|
||||
pub async fn user_get(
|
||||
info: web::Path<(UserId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.0;
|
||||
let user_data = crate::database::models::User::get(id.into(), &**pool)
|
||||
let user_data = User::get(id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
@ -50,6 +89,38 @@ pub async fn user_get(
|
||||
}
|
||||
}
|
||||
|
||||
#[get("{user_id}/mods")]
|
||||
pub async fn mods_list(
|
||||
info: web::Path<(UserId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.0.into();
|
||||
|
||||
let user_exists = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM users WHERE id = $1)",
|
||||
id as crate::database::models::UserId,
|
||||
)
|
||||
.fetch_one(&**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.exists;
|
||||
|
||||
if user_exists.unwrap_or(false) {
|
||||
let mod_data = User::get_mods(id, &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
let response = mod_data
|
||||
.into_iter()
|
||||
.map(|v| v.into())
|
||||
.collect::<Vec<crate::models::ids::ModId>>();
|
||||
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
} else {
|
||||
Ok(HttpResponse::NotFound().body(""))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make this actually do stuff
|
||||
#[delete("{id}")]
|
||||
pub async fn user_delete(
|
||||
|
||||
@ -15,6 +15,7 @@ use sqlx::postgres::PgPool;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct InitialVersionData {
|
||||
pub mod_id: ModId,
|
||||
pub file_parts: Vec<String>,
|
||||
pub version_number: String,
|
||||
pub version_title: String,
|
||||
@ -34,7 +35,6 @@ struct InitialFileData {
|
||||
#[post("version")]
|
||||
pub async fn version_create(
|
||||
req: HttpRequest,
|
||||
url_data: actix_web::web::Path<(ModId,)>,
|
||||
payload: Multipart,
|
||||
client: Data<PgPool>,
|
||||
file_host: Data<std::sync::Arc<dyn FileHost + Send + Sync>>,
|
||||
@ -42,15 +42,12 @@ pub async fn version_create(
|
||||
let mut transaction = client.begin().await?;
|
||||
let mut uploaded_files = Vec::new();
|
||||
|
||||
let mod_id = url_data.into_inner().0.into();
|
||||
|
||||
let result = version_create_inner(
|
||||
req,
|
||||
payload,
|
||||
&mut transaction,
|
||||
&***file_host,
|
||||
&mut uploaded_files,
|
||||
mod_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -77,7 +74,6 @@ async fn version_create_inner(
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
file_host: &dyn FileHost,
|
||||
uploaded_files: &mut Vec<UploadedFile>,
|
||||
mod_id: models::ModId,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let cdn_url = dotenv::var("CDN_URL")?;
|
||||
|
||||
@ -104,6 +100,7 @@ async fn version_create_inner(
|
||||
let version_create_data: InitialVersionData = serde_json::from_slice(&data)?;
|
||||
initial_version_data = Some(version_create_data);
|
||||
let version_create_data = initial_version_data.as_ref().unwrap();
|
||||
let mod_id: models::ModId = version_create_data.mod_id.into();
|
||||
|
||||
let results = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)",
|
||||
@ -154,8 +151,7 @@ async fn version_create_inner(
|
||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||
let body_url = format!(
|
||||
"data/{}/changelogs/{}/body.md",
|
||||
ModId::from(mod_id),
|
||||
version_id
|
||||
version_create_data.mod_id, version_id
|
||||
);
|
||||
|
||||
let uploaded_text = file_host
|
||||
@ -180,7 +176,7 @@ async fn version_create_inner(
|
||||
|
||||
version_builder = Some(VersionBuilder {
|
||||
version_id: version_id.into(),
|
||||
mod_id,
|
||||
mod_id: version_create_data.mod_id.into(),
|
||||
author_id: user.id.into(),
|
||||
name: version_create_data.version_title.clone(),
|
||||
version_number: version_create_data.version_number.clone(),
|
||||
@ -210,7 +206,7 @@ async fn version_create_inner(
|
||||
uploaded_files,
|
||||
&cdn_url,
|
||||
&content_disposition,
|
||||
ModId::from(mod_id),
|
||||
version.mod_id.into(),
|
||||
&version.version_number,
|
||||
)
|
||||
.await?;
|
||||
@ -272,7 +268,7 @@ async fn version_create_inner(
|
||||
#[post("{version_id}/file")]
|
||||
pub async fn upload_file_to_version(
|
||||
req: HttpRequest,
|
||||
url_data: actix_web::web::Path<(ModId, VersionId)>,
|
||||
url_data: actix_web::web::Path<(VersionId,)>,
|
||||
payload: Multipart,
|
||||
client: Data<PgPool>,
|
||||
file_host: Data<std::sync::Arc<dyn FileHost + Send + Sync>>,
|
||||
@ -280,9 +276,7 @@ pub async fn upload_file_to_version(
|
||||
let mut transaction = client.begin().await?;
|
||||
let mut uploaded_files = Vec::new();
|
||||
|
||||
let data = url_data.into_inner();
|
||||
let mod_id = models::ModId::from(data.0);
|
||||
let version_id = models::VersionId::from(data.1);
|
||||
let version_id = models::VersionId::from(url_data.0);
|
||||
|
||||
let result = upload_file_to_version_inner(
|
||||
req,
|
||||
@ -291,7 +285,6 @@ pub async fn upload_file_to_version(
|
||||
&***file_host,
|
||||
&mut uploaded_files,
|
||||
version_id,
|
||||
mod_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -319,7 +312,6 @@ async fn upload_file_to_version_inner(
|
||||
file_host: &dyn FileHost,
|
||||
uploaded_files: &mut Vec<UploadedFile>,
|
||||
version_id: models::VersionId,
|
||||
mod_id: models::ModId,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let cdn_url = dotenv::var("CDN_URL")?;
|
||||
|
||||
@ -347,11 +339,6 @@ async fn upload_file_to_version_inner(
|
||||
));
|
||||
}
|
||||
};
|
||||
if version.mod_id as u64 != mod_id.0 as u64 {
|
||||
return Err(CreateError::InvalidInput(
|
||||
"An invalid version id was supplied".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if version.author_id as u64 != user.id.0 {
|
||||
return Err(CreateError::InvalidInput("Unauthorized".to_string()));
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::auth::check_is_moderator_from_headers;
|
||||
use crate::database;
|
||||
use crate::models;
|
||||
use actix_web::{delete, get, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
// TODO: this needs filtering, and a better response type
|
||||
@ -42,12 +43,56 @@ pub async fn version_list(
|
||||
}
|
||||
}
|
||||
|
||||
#[get("{version_id}")]
|
||||
pub async fn version_get(
|
||||
info: web::Path<(models::ids::ModId, models::ids::VersionId)>,
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct VersionIds {
|
||||
pub ids: String,
|
||||
}
|
||||
|
||||
// TODO: Make this return the versions mod struct
|
||||
#[get("versions")]
|
||||
pub async fn versions_get(
|
||||
web::Query(ids): web::Query<VersionIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.1;
|
||||
let version_ids = serde_json::from_str::<Vec<models::ids::VersionId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
let versions_data = database::models::Version::get_many(version_ids, &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
use models::mods::VersionType;
|
||||
let versions: Vec<models::mods::Version> = versions_data
|
||||
.into_iter()
|
||||
.map(|data| models::mods::Version {
|
||||
id: data.id.into(),
|
||||
mod_id: data.mod_id.into(),
|
||||
author_id: data.author_id.into(),
|
||||
|
||||
name: data.name,
|
||||
version_number: data.version_number,
|
||||
changelog_url: data.changelog_url,
|
||||
date_published: data.date_published,
|
||||
downloads: data.downloads as u32,
|
||||
version_type: VersionType::Release,
|
||||
|
||||
files: vec![],
|
||||
dependencies: Vec::new(), // TODO: dependencies
|
||||
game_versions: vec![],
|
||||
loaders: vec![],
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(versions))
|
||||
}
|
||||
|
||||
#[get("{version_id}")]
|
||||
pub async fn version_get(
|
||||
info: web::Path<(models::ids::VersionId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.0;
|
||||
let version_data = database::models::Version::get_full(id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
@ -55,11 +100,6 @@ pub async fn version_get(
|
||||
if let Some(data) = version_data {
|
||||
use models::mods::VersionType;
|
||||
|
||||
if models::ids::ModId::from(data.mod_id) != info.0 {
|
||||
// Version doesn't belong to that mod
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
}
|
||||
|
||||
let response = models::mods::Version {
|
||||
id: data.id.into(),
|
||||
mod_id: data.mod_id.into(),
|
||||
|
||||
@ -41,6 +41,7 @@ pub struct IndexingSettings {
|
||||
}
|
||||
|
||||
impl IndexingSettings {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_env() -> Self {
|
||||
let index_local = true;
|
||||
let index_external = dotenv::var("INDEX_CURSEFORGE")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user