You've already forked AstralRinth
forked from didirus/AstralRinth
d477874535
* Improve curseforge and local indexing This should make curseforge indexing more efficient, and reuses some of the normal local indexing for the queued indexing of recently created mods. * Unify impls for single and multiple routes for mods and versions This uses the same backend for the single and multiple query routes so that they no longer return inconsistent information. * Cache valid curseforge mod ids to reduce request load This caches the ids of minecraft mods and reuses them on indexing to reduce the amount of unused addons that are returned.
170 lines
5.2 KiB
Rust
170 lines
5.2 KiB
Rust
use super::ApiError;
|
|
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
|
|
// Currently it only gives a list of ids, which have to be
|
|
// requested manually. This route could give a list of the
|
|
// ids as well as the supported versions and loaders, or
|
|
// other info that is needed for selecting the right version.
|
|
#[get("version")]
|
|
pub async fn version_list(
|
|
info: web::Path<(models::ids::ModId,)>,
|
|
pool: web::Data<PgPool>,
|
|
) -> Result<HttpResponse, ApiError> {
|
|
let id = info.into_inner().0.into();
|
|
|
|
let mod_exists = sqlx::query!(
|
|
"SELECT EXISTS(SELECT 1 FROM mods WHERE id = $1)",
|
|
id as database::models::ModId,
|
|
)
|
|
.fetch_one(&**pool)
|
|
.await
|
|
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
|
.exists;
|
|
|
|
if mod_exists.unwrap_or(false) {
|
|
let mod_data = database::models::Version::get_mod_versions(id, &**pool)
|
|
.await
|
|
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
|
|
|
let response = mod_data
|
|
.into_iter()
|
|
.map(|v| v.into())
|
|
.collect::<Vec<models::ids::VersionId>>();
|
|
|
|
Ok(HttpResponse::Ok().json(response))
|
|
} else {
|
|
Ok(HttpResponse::NotFound().body(""))
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct VersionIds {
|
|
pub ids: String,
|
|
}
|
|
|
|
#[get("versions")]
|
|
pub async fn versions_get(
|
|
web::Query(ids): web::Query<VersionIds>,
|
|
pool: web::Data<PgPool>,
|
|
) -> Result<HttpResponse, ApiError> {
|
|
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_full(version_ids, &**pool)
|
|
.await
|
|
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
|
|
|
let versions: Vec<models::mods::Version> = versions_data
|
|
.into_iter()
|
|
.filter_map(|v| v)
|
|
.map(convert_version)
|
|
.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.into_inner().0;
|
|
let version_data = database::models::Version::get_full(id.into(), &**pool)
|
|
.await
|
|
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
|
|
|
if let Some(data) = version_data {
|
|
Ok(HttpResponse::Ok().json(convert_version(data)))
|
|
} else {
|
|
Ok(HttpResponse::NotFound().body(""))
|
|
}
|
|
}
|
|
|
|
fn convert_version(data: database::models::version_item::QueryVersion) -> models::mods::Version {
|
|
use models::mods::VersionType;
|
|
|
|
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: match data.release_channel.as_str() {
|
|
"release" => VersionType::Release,
|
|
"beta" => VersionType::Beta,
|
|
"alpha" => VersionType::Alpha,
|
|
_ => VersionType::Alpha,
|
|
},
|
|
|
|
files: data
|
|
.files
|
|
.into_iter()
|
|
.map(|f| {
|
|
models::mods::VersionFile {
|
|
url: f.url,
|
|
filename: f.filename,
|
|
// FIXME: Hashes are currently stored as an ascii byte slice instead
|
|
// of as an actual byte array in the database
|
|
hashes: f
|
|
.hashes
|
|
.into_iter()
|
|
.map(|(k, v)| Some((k, String::from_utf8(v).ok()?)))
|
|
.collect::<Option<_>>()
|
|
.unwrap_or_else(Default::default),
|
|
}
|
|
})
|
|
.collect(),
|
|
dependencies: Vec::new(), // TODO: dependencies
|
|
game_versions: data
|
|
.game_versions
|
|
.into_iter()
|
|
.map(models::mods::GameVersion)
|
|
.collect(),
|
|
loaders: data
|
|
.loaders
|
|
.into_iter()
|
|
.map(models::mods::ModLoader)
|
|
.collect(),
|
|
}
|
|
}
|
|
|
|
#[delete("{version_id}")]
|
|
pub async fn version_delete(
|
|
req: HttpRequest,
|
|
info: web::Path<(models::ids::ModId, models::ids::VersionId)>,
|
|
pool: web::Data<PgPool>,
|
|
) -> Result<HttpResponse, ApiError> {
|
|
check_is_moderator_from_headers(
|
|
req.headers(),
|
|
&mut *pool
|
|
.acquire()
|
|
.await
|
|
.map_err(|e| ApiError::DatabaseError(e.into()))?,
|
|
)
|
|
.await
|
|
.map_err(|_| ApiError::AuthenticationError)?;
|
|
|
|
// TODO: check if the mod exists and matches the version id
|
|
let id = info.1;
|
|
let result = database::models::Version::remove_full(id.into(), &**pool)
|
|
.await
|
|
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
|
|
|
if result.is_some() {
|
|
Ok(HttpResponse::Ok().body(""))
|
|
} else {
|
|
Ok(HttpResponse::NotFound().body(""))
|
|
}
|
|
}
|