You've already forked pages
forked from didirus/AstralRinth
Fix deps, download URLs, remove duplicate deps (#310)
This commit is contained in:
@@ -29,25 +29,30 @@ impl DependencyBuilder {
|
||||
version_id: VersionId,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
let version_dependency_id = if let Some(project_id) = self.project_id {
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT version.id id FROM (
|
||||
SELECT DISTINCT ON(v.id) v.id, v.date_published FROM versions v
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id AND gvv.game_version_id IN (SELECT game_version_id FROM game_versions_versions WHERE joining_version_id = $2)
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id AND lv.loader_id IN (SELECT loader_id FROM loaders_versions WHERE version_id = $2)
|
||||
WHERE v.mod_id = $1
|
||||
) AS version
|
||||
ORDER BY version.date_published DESC
|
||||
LIMIT 1
|
||||
",
|
||||
project_id as ProjectId,
|
||||
version_id as VersionId,
|
||||
)
|
||||
.fetch_optional(&mut *transaction).await?.map(|x| VersionId(x.id))
|
||||
} else {
|
||||
self.version_id
|
||||
};
|
||||
let (version_dependency_id, project_dependency_id): (Option<VersionId>, Option<ProjectId>) =
|
||||
if self.version_id.is_some() {
|
||||
(self.version_id, None)
|
||||
} else if let Some(project_id) = self.project_id {
|
||||
let version_id = sqlx::query!(
|
||||
"
|
||||
SELECT version.id id FROM (
|
||||
SELECT DISTINCT ON(v.id) v.id, v.date_published FROM versions v
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id AND gvv.game_version_id IN (SELECT game_version_id FROM game_versions_versions WHERE joining_version_id = $2)
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id AND lv.loader_id IN (SELECT loader_id FROM loaders_versions WHERE version_id = $2)
|
||||
WHERE v.mod_id = $1
|
||||
) AS version
|
||||
ORDER BY version.date_published DESC
|
||||
LIMIT 1
|
||||
",
|
||||
project_id as ProjectId,
|
||||
version_id as VersionId,
|
||||
)
|
||||
.fetch_optional(&mut *transaction).await?.map(|x| VersionId(x.id));
|
||||
|
||||
(version_id, Some(project_id))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -57,7 +62,7 @@ impl DependencyBuilder {
|
||||
version_id as VersionId,
|
||||
self.dependency_type,
|
||||
version_dependency_id.map(|x| x.0),
|
||||
self.project_id.map(|x| x.0),
|
||||
project_dependency_id.map(|x| x.0),
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
@@ -26,20 +26,20 @@ pub enum ARError {
|
||||
}
|
||||
|
||||
impl ResponseError for ARError {
|
||||
fn error_response(&self) -> actix_web::web::HttpResponse {
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
match self {
|
||||
Self::LimitedError {
|
||||
max_requests,
|
||||
remaining,
|
||||
reset,
|
||||
} => {
|
||||
let mut response = actix_web::web::HttpResponse::TooManyRequests();
|
||||
let mut response = actix_web::HttpResponse::TooManyRequests();
|
||||
response.insert_header(("x-ratelimit-limit", max_requests.to_string()));
|
||||
response.insert_header(("x-ratelimit-remaining", remaining.to_string()));
|
||||
response.insert_header(("x-ratelimit-reset", reset.to_string()));
|
||||
response.body(self.to_string())
|
||||
}
|
||||
_ => actix_web::web::HttpResponse::build(self.status_code()).body(self.to_string()),
|
||||
_ => actix_web::HttpResponse::build(self.status_code()).body(self.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,25 +202,23 @@ impl actix_web::ResponseError for ApiError {
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> actix_web::web::HttpResponse {
|
||||
actix_web::web::HttpResponse::build(self.status_code()).json(
|
||||
crate::models::error::ApiError {
|
||||
error: match self {
|
||||
ApiError::EnvError(..) => "environment_error",
|
||||
ApiError::SqlxDatabaseError(..) => "database_error",
|
||||
ApiError::DatabaseError(..) => "database_error",
|
||||
ApiError::AuthenticationError(..) => "unauthorized",
|
||||
ApiError::CustomAuthenticationError(..) => "unauthorized",
|
||||
ApiError::XmlError(..) => "xml_error",
|
||||
ApiError::JsonError(..) => "json_error",
|
||||
ApiError::SearchError(..) => "search_error",
|
||||
ApiError::IndexingError(..) => "indexing_error",
|
||||
ApiError::FileHostingError(..) => "file_hosting_error",
|
||||
ApiError::InvalidInputError(..) => "invalid_input",
|
||||
ApiError::ValidationError(..) => "invalid_input",
|
||||
},
|
||||
description: &self.to_string(),
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
actix_web::HttpResponse::build(self.status_code()).json(crate::models::error::ApiError {
|
||||
error: match self {
|
||||
ApiError::EnvError(..) => "environment_error",
|
||||
ApiError::SqlxDatabaseError(..) => "database_error",
|
||||
ApiError::DatabaseError(..) => "database_error",
|
||||
ApiError::AuthenticationError(..) => "unauthorized",
|
||||
ApiError::CustomAuthenticationError(..) => "unauthorized",
|
||||
ApiError::XmlError(..) => "xml_error",
|
||||
ApiError::JsonError(..) => "json_error",
|
||||
ApiError::SearchError(..) => "search_error",
|
||||
ApiError::IndexingError(..) => "indexing_error",
|
||||
ApiError::FileHostingError(..) => "file_hosting_error",
|
||||
ApiError::InvalidInputError(..) => "invalid_input",
|
||||
ApiError::ValidationError(..) => "invalid_input",
|
||||
},
|
||||
)
|
||||
description: &self.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ pub async fn mod_search(
|
||||
.hits
|
||||
.into_iter()
|
||||
.map(|x| ResultSearchMod {
|
||||
mod_id: format!("local-{}", x.project_id.clone()),
|
||||
mod_id: format!("local-{}", x.project_id),
|
||||
slug: x.slug,
|
||||
author: x.author.clone(),
|
||||
title: x.title,
|
||||
|
||||
@@ -34,7 +34,10 @@ pub struct InitialVersionData {
|
||||
pub version_title: String,
|
||||
#[validate(length(max = 65536))]
|
||||
pub version_body: Option<String>,
|
||||
#[validate(length(min = 0, max = 256))]
|
||||
#[validate(
|
||||
length(min = 0, max = 256),
|
||||
custom(function = "crate::util::validate::validate_deps")
|
||||
)]
|
||||
pub dependencies: Vec<Dependency>,
|
||||
#[validate(length(min = 1))]
|
||||
pub game_versions: Vec<GameVersion>,
|
||||
@@ -630,22 +633,24 @@ pub async fn upload_file(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let file_name_encode = format!(
|
||||
"data/{}/versions/{}/{}",
|
||||
project_id,
|
||||
version_number,
|
||||
urlencoding::encode(file_name)
|
||||
);
|
||||
let file_name = format!(
|
||||
"data/{}/versions/{}/{}",
|
||||
project_id, version_number, &file_name
|
||||
);
|
||||
|
||||
let upload_data = file_host
|
||||
.upload_file(
|
||||
content_type,
|
||||
&format!(
|
||||
"data/{}/versions/{}/{}",
|
||||
project_id,
|
||||
version_number,
|
||||
urlencoding::encode(&file_name)
|
||||
),
|
||||
data.freeze(),
|
||||
)
|
||||
.upload_file(content_type, &file_name, data.freeze())
|
||||
.await?;
|
||||
|
||||
uploaded_files.push(UploadedFile {
|
||||
file_id: upload_data.file_id,
|
||||
file_name: upload_data.file_name.clone(),
|
||||
file_name: file_name_encode,
|
||||
});
|
||||
|
||||
// TODO: Malware scan + file validation
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use super::ApiError;
|
||||
use crate::database::models::version_item::QueryVersion;
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::{models, database};
|
||||
use crate::models::projects::{GameVersion, Loader, Version};
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::util::auth::get_user_from_headers;
|
||||
use crate::util::routes::ok_or_not_found;
|
||||
use crate::{database, models};
|
||||
use actix_web::{delete, get, post, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
@@ -171,6 +171,10 @@ pub struct EditVersion {
|
||||
#[validate(length(max = 65536))]
|
||||
pub changelog: Option<String>,
|
||||
pub version_type: Option<models::projects::VersionType>,
|
||||
#[validate(
|
||||
length(min = 0, max = 256),
|
||||
custom(function = "crate::util::validate::validate_deps")
|
||||
)]
|
||||
pub dependencies: Option<Vec<Dependency>>,
|
||||
pub game_versions: Option<Vec<models::projects::GameVersion>>,
|
||||
pub loaders: Option<Vec<models::projects::Loader>>,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::models::error::ApiError;
|
||||
use crate::models::projects::SearchRequest;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::HttpResponse;
|
||||
use actix_web::HttpResponse;
|
||||
use chrono::{DateTime, Utc};
|
||||
use meilisearch_sdk::client::Client;
|
||||
use meilisearch_sdk::document::Document;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use validator::{ValidationErrors, ValidationErrorsKind};
|
||||
@@ -53,3 +54,26 @@ pub fn validation_errors_to_string(errors: ValidationErrors, adder: Option<Strin
|
||||
|
||||
String::new()
|
||||
}
|
||||
|
||||
pub fn validate_deps(
|
||||
values: &[crate::models::projects::Dependency],
|
||||
) -> Result<(), validator::ValidationError> {
|
||||
if values
|
||||
.iter()
|
||||
.duplicates_by(|x| {
|
||||
format!(
|
||||
"{}-{}",
|
||||
x.version_id
|
||||
.unwrap_or(crate::models::projects::VersionId(0)),
|
||||
x.project_id
|
||||
.unwrap_or(crate::models::projects::ProjectId(0))
|
||||
)
|
||||
})
|
||||
.next()
|
||||
.is_some()
|
||||
{
|
||||
return Err(validator::ValidationError::new("duplicate dependency"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ pub enum SupportedGameVersions {
|
||||
All,
|
||||
PastDate(DateTime<Utc>),
|
||||
Range(DateTime<Utc>, DateTime<Utc>),
|
||||
#[allow(dead_code)] Custom(Vec<GameVersion>),
|
||||
#[allow(dead_code)]
|
||||
Custom(Vec<GameVersion>),
|
||||
}
|
||||
|
||||
pub trait Validator: Sync {
|
||||
|
||||
@@ -33,7 +33,7 @@ pub struct PackFile<'a> {
|
||||
pub downloads: Vec<&'a str>,
|
||||
}
|
||||
|
||||
fn validate_download_url(values: &Vec<&str>) -> Result<(), validator::ValidationError> {
|
||||
fn validate_download_url(values: &[&str]) -> Result<(), validator::ValidationError> {
|
||||
for value in values {
|
||||
let url = url::Url::parse(value)
|
||||
.ok()
|
||||
|
||||
Reference in New Issue
Block a user