Move validators to seperate thread, other fixes (#253)
* Move validators to seperate thread, other fixes * Update rust version in Dockerfile * Fix notifs not working * Fix pack validator not enforcing files
This commit is contained in:
parent
13187de97d
commit
7f791d4919
@ -1,4 +1,4 @@
|
||||
FROM rust:1.52.1 as build
|
||||
FROM rust:1.55.0 as build
|
||||
ENV PKG_CONFIG_ALLOW_CROSS=1
|
||||
|
||||
WORKDIR /usr/src/labrinth
|
||||
|
||||
222
sqlx-data.json
222
sqlx-data.json
@ -1707,68 +1707,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"59db001df11e9730d089eca19a73ca6f934c6fd199e6411955072c767f9a2551": {
|
||||
"query": "\n SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type,\n ARRAY_AGG(DISTINCT na.id || ', ' || na.title || ', ' || na.action_route || ', ' || na.action_route_method) actions\n FROM notifications n\n LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id\n WHERE n.id = $1\n GROUP BY n.id, n.user_id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "user_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "text",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "link",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "created",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "read",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "notification_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "actions",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null
|
||||
]
|
||||
}
|
||||
},
|
||||
"5a03c653f1ff3339a01422ee4267a66157e6da9a51cc7d9beb0f87d59c3a444c": {
|
||||
"query": "\n SELECT d.dependent_id, d.dependency_id, d.mod_dependency_id\n FROM versions v\n INNER JOIN dependencies d ON d.dependent_id = v.id\n WHERE v.mod_id = $1\n ",
|
||||
"describe": {
|
||||
@ -2763,26 +2701,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"7e73d3a17807f57ba6def5ff718e6dcb3a65ef8da653d839560b24635334cf05": {
|
||||
"query": "\n SELECT m.title FROM mods m\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"7f1696cee355c03f474fda2283669c60046833db88b3e2befd62a1fea7a12c70": {
|
||||
"query": "\n INSERT INTO downloads (\n version_id, identifier\n )\n VALUES (\n $1, $2\n )\n ",
|
||||
"describe": {
|
||||
@ -3105,32 +3023,6 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"8c25a870b9306d653caaa4c324122ecd928796107b9d2fcdeaba82c7fcbbbebc": {
|
||||
"query": "\n SELECT m.title, m.id FROM mods m\n WHERE m.team_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"8d491f3ccbddbd1e1bbea62d04090b2214d10182e3bfac7d8374ac183514f352": {
|
||||
"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 STRING_AGG(DISTINCT mg.image_url, ',') gallery\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 LEFT OUTER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id\n LEFT OUTER JOIN game_versions gv ON gvv.game_version_id = gv.id\n LEFT OUTER JOIN loaders_versions lv ON lv.version_id = v.id\n LEFT OUTER JOIN loaders lo ON lo.id = lv.loader_id\n LEFT OUTER JOIN mods_gallery mg ON mg.mod_id = m.id\n INNER JOIN statuses s ON s.id = m.status\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": {
|
||||
@ -3303,6 +3195,38 @@
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"9348309884811e8b22f33786ae7c0f259f37f3c90e545f00761a641570107160": {
|
||||
"query": "\n SELECT m.title title, m.id id, pt.name project_type\n FROM mods m\n INNER JOIN project_types pt ON pt.id = m.project_type\n WHERE m.team_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "project_type",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"94a823b6e8b2610d72843008706c448432aab21690b4727aea77ad687a98f634": {
|
||||
"query": "\n DELETE FROM dependencies WHERE mod_dependency_id = NULL AND dependency_id = NULL\n ",
|
||||
"describe": {
|
||||
@ -3871,6 +3795,32 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"adbe17a5ad3cea333b30b5d6111aff713a8f7dc79ded21f5ba942c4f1108aa8f": {
|
||||
"query": "\n SELECT m.title title, pt.name project_type\n FROM mods m\n INNER JOIN project_types pt ON pt.id = m.project_type\n WHERE m.id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "project_type",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
}
|
||||
},
|
||||
"b030a9e0fdb75eee8ee50aafdcb6063a073e2aa53cc70d40ed46437c1d0dfe80": {
|
||||
"query": "\n INSERT INTO mods_gallery (\n mod_id, image_url, featured, title, description\n )\n VALUES (\n $1, $2, $3, $4, $5\n )\n ",
|
||||
"describe": {
|
||||
@ -5642,6 +5592,62 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"ea96ab7c1290f4caddcef8ecf2aec0216654faca05ff760ffa553ad3e32827f5": {
|
||||
"query": "\n SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type\n FROM notifications n\n WHERE n.id = $1\n GROUP BY n.id, n.user_id;\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "user_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "title",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "text",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "link",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "created",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "read",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "notification_type",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
}
|
||||
},
|
||||
"ebef881a0dae70e990814e567ed3de9565bb29b772782bc974c953af195fd6d7": {
|
||||
"query": "\n SELECT n.id FROM notifications n\n WHERE n.user_id = $1\n ",
|
||||
"describe": {
|
||||
|
||||
@ -14,6 +14,7 @@ pub struct Loader {
|
||||
pub supported_project_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GameVersion {
|
||||
pub id: GameVersionId,
|
||||
pub version: String,
|
||||
|
||||
@ -123,10 +123,8 @@ impl Notification {
|
||||
let (notifications, actions) = futures::join!(
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type,
|
||||
ARRAY_AGG(DISTINCT na.id || ', ' || na.title || ', ' || na.action_route || ', ' || na.action_route_method) actions
|
||||
SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type
|
||||
FROM notifications n
|
||||
LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id
|
||||
WHERE n.id = $1
|
||||
GROUP BY n.id, n.user_id;
|
||||
",
|
||||
|
||||
@ -662,7 +662,7 @@ impl Version {
|
||||
for hash in hashes? {
|
||||
let entry = hashes_map
|
||||
.entry(FileId(hash.file_id))
|
||||
.or_insert(HashMap::new());
|
||||
.or_insert_with(HashMap::new);
|
||||
|
||||
if let Some(raw_hash) = hash.hash {
|
||||
entry.insert(hash.algorithm, raw_hash.into_bytes());
|
||||
@ -695,8 +695,8 @@ impl Version {
|
||||
dependencies: dependencies?
|
||||
.into_iter()
|
||||
.map(|x| QueryDependency {
|
||||
project_id: x.mod_dependency_id.map(|x| ProjectId(x)),
|
||||
version_id: x.dependency_id.map(|x| VersionId(x)),
|
||||
project_id: x.mod_dependency_id.map(ProjectId),
|
||||
version_id: x.dependency_id.map(VersionId),
|
||||
dependency_type: x.dependency_type,
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@ -10,8 +10,8 @@ pub struct PodInfo {
|
||||
impl PodInfo {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pod_name: dotenv::var("POD_NAME").unwrap_or("DEV".to_string()),
|
||||
node_name: dotenv::var("NODE_NAME").unwrap_or("self-hosted".to_string()),
|
||||
pod_name: dotenv::var("POD_NAME").unwrap_or_else(|_| "DEV".to_string()),
|
||||
node_name: dotenv::var("NODE_NAME").unwrap_or_else(|_| "self-hosted".to_string()),
|
||||
pod_id: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ where
|
||||
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
// The request has started.
|
||||
let pattern_or_path = req.match_pattern().unwrap_or("unknown".to_string());
|
||||
let pattern_or_path = req.match_pattern().unwrap_or_else(|| "unknown".to_string());
|
||||
let counter = self
|
||||
.counters
|
||||
.current_requests
|
||||
|
||||
@ -129,7 +129,7 @@ pub fn teams_config(cfg: &mut web::ServiceConfig) {
|
||||
|
||||
pub fn notifications_config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(notifications::notifications_get);
|
||||
cfg.service(notifications::notification_delete);
|
||||
cfg.service(notifications::notifications_delete);
|
||||
|
||||
cfg.service(
|
||||
web::scope("notification")
|
||||
|
||||
@ -510,7 +510,7 @@ pub async fn project_create_inner(
|
||||
&*project_create_data.project_type,
|
||||
version_data.loaders.clone(),
|
||||
version_data.game_versions.clone(),
|
||||
&all_game_versions,
|
||||
all_game_versions.clone(),
|
||||
false,
|
||||
&mut transaction,
|
||||
)
|
||||
|
||||
@ -373,7 +373,7 @@ pub async fn project_edit(
|
||||
}
|
||||
}
|
||||
|
||||
let status_id = database::models::StatusId::get_id(&status, &mut *transaction)
|
||||
let status_id = database::models::StatusId::get_id(status, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
@ -423,17 +423,15 @@ pub async fn project_edit(
|
||||
.await?;
|
||||
|
||||
for category in categories {
|
||||
let category_id = database::models::categories::Category::get_id(
|
||||
&category,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Category {} does not exist.",
|
||||
category.clone()
|
||||
))
|
||||
})?;
|
||||
let category_id =
|
||||
database::models::categories::Category::get_id(category, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Category {} does not exist.",
|
||||
category.clone()
|
||||
))
|
||||
})?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
|
||||
@ -218,7 +218,9 @@ pub async fn add_team_member(
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT m.title, m.id FROM mods m
|
||||
SELECT m.title title, m.id id, pt.name project_type
|
||||
FROM mods m
|
||||
INNER JOIN project_types pt ON pt.id = m.project_type
|
||||
WHERE m.team_id = $1
|
||||
",
|
||||
team_id as crate::database::models::ids::TeamId
|
||||
@ -234,7 +236,7 @@ pub async fn add_team_member(
|
||||
"Team invite from {} to join the team for project {}",
|
||||
current_user.username, result.title
|
||||
),
|
||||
link: format!("project/{}", ProjectId(result.id as u64)),
|
||||
link: format!("/{}/{}", result.project_type, ProjectId(result.id as u64)),
|
||||
actions: vec![
|
||||
NotificationActionBuilder {
|
||||
title: "Accept".to_string(),
|
||||
@ -361,7 +363,7 @@ pub async fn transfer_ownership(
|
||||
TeamMember::edit_team_member(
|
||||
id.into(),
|
||||
current_user.id.into(),
|
||||
None,
|
||||
Some(Permissions::ALL),
|
||||
Some(crate::models::teams::DEFAULT_ROLE.to_string()),
|
||||
None,
|
||||
&mut transaction,
|
||||
|
||||
@ -323,8 +323,7 @@ pub async fn user_icon_edit(
|
||||
}
|
||||
|
||||
let bytes =
|
||||
read_from_payload(&mut payload, 262144, "Icons must be smaller than 256KiB")
|
||||
.await?;
|
||||
read_from_payload(&mut payload, 2097152, "Icons must be smaller than 2MiB").await?;
|
||||
|
||||
let upload_data = file_host
|
||||
.upload_file(
|
||||
|
||||
@ -288,7 +288,7 @@ async fn version_create_inner(
|
||||
&*project_type,
|
||||
version_data.loaders,
|
||||
version_data.game_versions,
|
||||
&all_game_versions,
|
||||
all_game_versions.clone(),
|
||||
false,
|
||||
&mut transaction,
|
||||
)
|
||||
@ -308,8 +308,10 @@ async fn version_create_inner(
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT m.title FROM mods m
|
||||
WHERE id = $1
|
||||
SELECT m.title title, pt.name project_type
|
||||
FROM mods m
|
||||
INNER JOIN project_types pt ON pt.id = m.project_type
|
||||
WHERE m.id = $1
|
||||
",
|
||||
builder.project_id as crate::database::models::ids::ProjectId
|
||||
)
|
||||
@ -344,7 +346,10 @@ async fn version_create_inner(
|
||||
result.title,
|
||||
version_data.version_number.clone()
|
||||
),
|
||||
link: format!("project/{}/version/{}", project_id, version_id),
|
||||
link: format!(
|
||||
"/{}/{}/version/{}",
|
||||
result.project_type, project_id, version_id
|
||||
),
|
||||
actions: vec![],
|
||||
}
|
||||
.insert_many(users, &mut *transaction)
|
||||
@ -544,7 +549,7 @@ async fn upload_file_to_version_inner(
|
||||
.into_iter()
|
||||
.map(GameVersion)
|
||||
.collect(),
|
||||
&all_game_versions,
|
||||
all_game_versions.clone(),
|
||||
true,
|
||||
&mut transaction,
|
||||
)
|
||||
@ -579,7 +584,7 @@ pub async fn upload_file(
|
||||
project_type: &str,
|
||||
loaders: Vec<Loader>,
|
||||
game_versions: Vec<GameVersion>,
|
||||
all_game_versions: &[models::categories::GameVersion],
|
||||
all_game_versions: Vec<models::categories::GameVersion>,
|
||||
ignore_primary: bool,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<(), CreateError> {
|
||||
@ -614,13 +619,14 @@ pub async fn upload_file(
|
||||
}
|
||||
|
||||
let validation_result = validate_file(
|
||||
&data,
|
||||
file_extension,
|
||||
project_type,
|
||||
data.clone().into(),
|
||||
file_extension.to_string(),
|
||||
project_type.to_string(),
|
||||
loaders,
|
||||
game_versions,
|
||||
all_game_versions,
|
||||
)?;
|
||||
)
|
||||
.await?;
|
||||
|
||||
let upload_data = file_host
|
||||
.upload_file(
|
||||
|
||||
@ -259,8 +259,8 @@ pub async fn version_edit(
|
||||
let builders = dependencies
|
||||
.iter()
|
||||
.map(|x| database::models::version_item::DependencyBuilder {
|
||||
project_id: x.project_id.clone().map(|x| x.into()),
|
||||
version_id: x.version_id.clone().map(|x| x.into()),
|
||||
project_id: x.project_id.map(|x| x.into()),
|
||||
version_id: x.version_id.map(|x| x.into()),
|
||||
dependency_type: x.dependency_type.to_string(),
|
||||
})
|
||||
.collect::<Vec<database::models::version_item::DependencyBuilder>>();
|
||||
|
||||
@ -80,7 +80,7 @@ async fn update_index_helper<'a>(
|
||||
name: &'static str,
|
||||
rule: &'static str,
|
||||
) -> Result<Index<'a>, IndexingError> {
|
||||
update_index(&client, name, {
|
||||
update_index(client, name, {
|
||||
let mut rules = default_rules();
|
||||
rules.push_back(rule);
|
||||
rules.into()
|
||||
@ -159,11 +159,11 @@ async fn add_to_index(index: Index<'_>, mods: &[UploadSearchProject]) -> Result<
|
||||
|
||||
async fn create_and_add_to_index<'a>(
|
||||
client: &'a Client<'a>,
|
||||
projects: &'a Vec<UploadSearchProject>,
|
||||
projects: &'a [UploadSearchProject],
|
||||
name: &'static str,
|
||||
rule: &'static str,
|
||||
) -> Result<(), IndexingError> {
|
||||
let index = create_index(&client, name, || {
|
||||
let index = create_index(client, name, || {
|
||||
let mut relevance_rules = default_rules();
|
||||
relevance_rules.push_back(rule);
|
||||
relevance_rules.into()
|
||||
|
||||
@ -28,7 +28,7 @@ impl super::Validator for FabricValidator {
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
archive.by_name("fabric.mod.json").map_err(|_| {
|
||||
ValidationError::InvalidInputError("No fabric.mod.json present for Fabric file.".into())
|
||||
@ -39,7 +39,7 @@ impl super::Validator for FabricValidator {
|
||||
.any(|name| name.ends_with("refmap.json") || name.ends_with(".class"))
|
||||
{
|
||||
return Ok(ValidationResult::Warning(
|
||||
"Fabric mod file is a source file!".into(),
|
||||
"Fabric mod file is a source file!",
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ impl super::Validator for ForgeValidator {
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
archive.by_name("META-INF/mods.toml").map_err(|_| {
|
||||
ValidationError::InvalidInputError("No mods.toml present for Forge file.".into())
|
||||
@ -36,7 +36,7 @@ impl super::Validator for ForgeValidator {
|
||||
|
||||
if !archive.file_names().any(|name| name.ends_with(".class")) {
|
||||
return Ok(ValidationResult::Warning(
|
||||
"Forge mod file is a source file!".into(),
|
||||
"Forge mod file is a source file!",
|
||||
));
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ impl super::Validator for LegacyForgeValidator {
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
archive.by_name("mcmod.info").map_err(|_| {
|
||||
ValidationError::InvalidInputError("No mcmod.info present for Forge file.".into())
|
||||
@ -79,7 +79,7 @@ impl super::Validator for LegacyForgeValidator {
|
||||
|
||||
if !archive.file_names().any(|name| name.ends_with(".class")) {
|
||||
return Ok(ValidationResult::Warning(
|
||||
"Forge mod file is a source file!".into(),
|
||||
"Forge mod file is a source file!",
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@ pub enum ValidationError {
|
||||
SerdeError(#[from] serde_json::Error),
|
||||
#[error("Invalid Input: {0}")]
|
||||
InvalidInputError(std::borrow::Cow<'static, str>),
|
||||
#[error("Error while managing threads")]
|
||||
BlockingError,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
@ -45,7 +47,7 @@ pub trait Validator: Sync {
|
||||
fn get_supported_game_versions(&self) -> SupportedGameVersions;
|
||||
fn validate(
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||
) -> Result<ValidationResult, ValidationError>;
|
||||
}
|
||||
|
||||
@ -57,48 +59,52 @@ static VALIDATORS: [&dyn Validator; 4] = [
|
||||
];
|
||||
|
||||
/// The return value is whether this file should be marked as primary or not, based on the analysis of the file
|
||||
pub fn validate_file(
|
||||
data: &[u8],
|
||||
file_extension: &str,
|
||||
project_type: &str,
|
||||
pub async fn validate_file(
|
||||
data: bytes::Bytes,
|
||||
file_extension: String,
|
||||
project_type: String,
|
||||
loaders: Vec<Loader>,
|
||||
game_versions: Vec<GameVersion>,
|
||||
all_game_versions: &[crate::database::models::categories::GameVersion],
|
||||
all_game_versions: Vec<crate::database::models::categories::GameVersion>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
let reader = std::io::Cursor::new(data);
|
||||
let mut zip = zip::ZipArchive::new(reader)?;
|
||||
Ok(actix_web::web::block(move || {
|
||||
let reader = std::io::Cursor::new(data);
|
||||
let mut zip = zip::ZipArchive::new(reader)?;
|
||||
|
||||
let mut visited = false;
|
||||
for validator in &VALIDATORS {
|
||||
if validator.get_project_types().contains(&project_type)
|
||||
&& loaders
|
||||
.iter()
|
||||
.any(|x| validator.get_supported_loaders().contains(&&*x.0))
|
||||
&& game_version_supported(
|
||||
&game_versions,
|
||||
all_game_versions,
|
||||
validator.get_supported_game_versions(),
|
||||
)
|
||||
{
|
||||
if validator.get_file_extensions().contains(&file_extension) {
|
||||
return validator.validate(&mut zip);
|
||||
} else {
|
||||
visited = true;
|
||||
let mut visited = false;
|
||||
for validator in &VALIDATORS {
|
||||
if validator.get_project_types().contains(&&*project_type)
|
||||
&& loaders
|
||||
.iter()
|
||||
.any(|x| validator.get_supported_loaders().contains(&&*x.0))
|
||||
&& game_version_supported(
|
||||
&game_versions,
|
||||
&all_game_versions,
|
||||
validator.get_supported_game_versions(),
|
||||
)
|
||||
{
|
||||
if validator.get_file_extensions().contains(&&*file_extension) {
|
||||
return validator.validate(&mut zip);
|
||||
} else {
|
||||
visited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if visited {
|
||||
Err(ValidationError::InvalidInputError(
|
||||
format!(
|
||||
"File extension {} is invalid for input file",
|
||||
file_extension
|
||||
)
|
||||
.into(),
|
||||
))
|
||||
} else {
|
||||
Ok(ValidationResult::Pass)
|
||||
}
|
||||
if visited {
|
||||
Err(ValidationError::InvalidInputError(
|
||||
format!(
|
||||
"File extension {} is invalid for input file",
|
||||
file_extension
|
||||
)
|
||||
.into(),
|
||||
))
|
||||
} else {
|
||||
Ok(ValidationResult::Pass)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.map_err(|_| ValidationError::BlockingError)?)
|
||||
}
|
||||
|
||||
fn game_version_supported(
|
||||
|
||||
@ -1,19 +1,60 @@
|
||||
use crate::models::projects::SideType;
|
||||
use crate::validate::{SupportedGameVersions, ValidationError, ValidationResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::{Cursor, Read};
|
||||
use zip::ZipArchive;
|
||||
use validator::Validate;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Validate)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PackFormat<'a> {
|
||||
pub game: &'a str,
|
||||
pub format_version: i32,
|
||||
#[validate(length(min = 3, max = 512))]
|
||||
pub version_id: &'a str,
|
||||
#[validate(length(min = 3, max = 512))]
|
||||
pub name: &'a str,
|
||||
#[validate(length(max = 2048))]
|
||||
pub summary: Option<&'a str>,
|
||||
#[validate]
|
||||
pub files: Vec<PackFile<'a>>,
|
||||
pub dependencies: std::collections::HashMap<PackDependency, &'a str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Validate)]
|
||||
pub struct PackFile<'a> {
|
||||
pub path: &'a str,
|
||||
pub hashes: std::collections::HashMap<FileHash, &'a str>,
|
||||
pub env: std::collections::HashMap<EnvType, SideType>,
|
||||
#[validate(custom(function = "validate_download_url"))]
|
||||
pub downloads: Vec<&'a str>,
|
||||
}
|
||||
|
||||
fn validate_download_url(values: &Vec<&str>) -> Result<(), validator::ValidationError> {
|
||||
for value in values {
|
||||
if !validator::validate_url(*value) {
|
||||
return Err(validator::ValidationError::new("invalid URL"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum FileHash {
|
||||
Sha1,
|
||||
Sha512,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum EnvType {
|
||||
Client,
|
||||
Server,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum PackDependency {
|
||||
@ -60,7 +101,7 @@ impl super::Validator for PackValidator {
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
let mut file = archive
|
||||
.by_name("index.json")
|
||||
@ -71,6 +112,10 @@ impl super::Validator for PackValidator {
|
||||
|
||||
let pack: PackFormat = serde_json::from_str(&contents)?;
|
||||
|
||||
pack
|
||||
.validate()
|
||||
.map_err(|err| ValidationError::InvalidInputError(validation_errors_to_string(err, None).into()))?;
|
||||
|
||||
if pack.game != "minecraft" {
|
||||
return Err(ValidationError::InvalidInputError(
|
||||
format!("Game {0} does not exist!", pack.game).into(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user