chore(clippy): enable and fix many stricter lints (#3783)
* chore(clippy): enable and fix many stricter lints These ensure that the codebase uses more idiomatic, performant, and concise language constructions. * chore: make non-Clippy compiler warnings also deny by default
This commit is contained in:
parent
301967d204
commit
f84f8c1c2b
45
Cargo.toml
45
Cargo.toml
@ -174,15 +174,42 @@ zip = { version = "4.0.0", default-features = false, features = [
|
|||||||
zxcvbn = "3.1.0"
|
zxcvbn = "3.1.0"
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
# Turn most warnings into errors by default
|
bool_to_int_with_if = "warn"
|
||||||
all = { level = "deny", priority = -1 }
|
borrow_as_ptr = "warn"
|
||||||
clear_with_drain = "deny"
|
cfg_not_test = "warn"
|
||||||
large_stack_arrays = "deny"
|
clear_with_drain = "warn"
|
||||||
negative_feature_names = "deny"
|
cloned_instead_of_copied = "warn"
|
||||||
non_std_lazy_statics = "deny"
|
collection_is_never_read = "warn"
|
||||||
read_zero_byte_vec = "deny"
|
dbg_macro = "warn"
|
||||||
redundant_feature_names = "deny"
|
default_trait_access = "warn"
|
||||||
wildcard_dependencies = "deny"
|
explicit_iter_loop = "warn"
|
||||||
|
filter_map_next = "warn"
|
||||||
|
flat_map_option = "warn"
|
||||||
|
format_push_string = "warn"
|
||||||
|
get_unwrap = "warn"
|
||||||
|
large_include_file = "warn"
|
||||||
|
large_stack_arrays = "warn"
|
||||||
|
manual_assert = "warn"
|
||||||
|
manual_instant_elapsed = "warn"
|
||||||
|
manual_is_variant_and = "warn"
|
||||||
|
manual_let_else = "warn"
|
||||||
|
map_unwrap_or = "warn"
|
||||||
|
match_bool = "warn"
|
||||||
|
needless_collect = "warn"
|
||||||
|
negative_feature_names = "warn"
|
||||||
|
non_std_lazy_statics = "warn"
|
||||||
|
pathbuf_init_then_push = "warn"
|
||||||
|
read_zero_byte_vec = "warn"
|
||||||
|
redundant_clone = "warn"
|
||||||
|
redundant_feature_names = "warn"
|
||||||
|
redundant_type_annotations = "warn"
|
||||||
|
todo = "warn"
|
||||||
|
unnested_or_patterns = "warn"
|
||||||
|
wildcard_dependencies = "warn"
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
# Turn warnings into errors by default
|
||||||
|
warnings = "deny"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
wry = { git = "https://github.com/modrinth/wry", rev = "cafdaa9" }
|
wry = { git = "https://github.com/modrinth/wry", rev = "cafdaa9" }
|
||||||
|
|||||||
@ -37,6 +37,7 @@ pub fn get_os() -> OS {
|
|||||||
let os = OS::MacOS;
|
let os = OS::MacOS;
|
||||||
os
|
os
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum OS {
|
pub enum OS {
|
||||||
|
|||||||
@ -43,7 +43,7 @@ pub async fn get_recent_worlds<R: Runtime>(
|
|||||||
display_statuses.unwrap_or(EnumSet::all()),
|
display_statuses.unwrap_or(EnumSet::all()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
for world in result.iter_mut() {
|
for world in &mut result {
|
||||||
adapt_world_icon(&app_handle, &mut world.world);
|
adapt_world_icon(&app_handle, &mut world.world);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -55,7 +55,7 @@ pub async fn get_profile_worlds<R: Runtime>(
|
|||||||
path: &str,
|
path: &str,
|
||||||
) -> Result<Vec<World>> {
|
) -> Result<Vec<World>> {
|
||||||
let mut result = worlds::get_profile_worlds(path).await?;
|
let mut result = worlds::get_profile_worlds(path).await?;
|
||||||
for world in result.iter_mut() {
|
for world in &mut result {
|
||||||
adapt_world_icon(&app_handle, world);
|
adapt_world_icon(&app_handle, world);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@ -229,7 +229,6 @@ fn main() {
|
|||||||
tauri::async_runtime::spawn(api::utils::handle_command(
|
tauri::async_runtime::spawn(api::utils::handle_command(
|
||||||
payload,
|
payload,
|
||||||
));
|
));
|
||||||
dbg!(url);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
@ -273,8 +272,9 @@ fn main() {
|
|||||||
|
|
||||||
match app {
|
match app {
|
||||||
Ok(app) => {
|
Ok(app) => {
|
||||||
#[allow(unused_variables)]
|
|
||||||
app.run(|app, event| {
|
app.run(|app, event| {
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
drop((app, event));
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
if let tauri::RunEvent::Opened { urls } = event {
|
if let tauri::RunEvent::Opened { urls } = event {
|
||||||
tracing::info!("Handling webview open {urls:?}");
|
tracing::info!("Handling webview open {urls:?}");
|
||||||
|
|||||||
@ -52,8 +52,7 @@ pub async fn fetch(
|
|||||||
if modrinth_version
|
if modrinth_version
|
||||||
.original_sha1
|
.original_sha1
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|x| x == &version.sha1)
|
.is_some_and(|x| x == &version.sha1)
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
existing_versions.push(modrinth_version);
|
existing_versions.push(modrinth_version);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -100,10 +100,7 @@ pub async fn filter_visible_project_ids(
|
|||||||
project.status.is_searchable()
|
project.status.is_searchable()
|
||||||
} else {
|
} else {
|
||||||
!project.status.is_hidden()
|
!project.status.is_hidden()
|
||||||
}) || user_option
|
}) || user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.role.is_mod())
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
return_projects.push(project.id);
|
return_projects.push(project.id);
|
||||||
} else if user_option.is_some() {
|
} else if user_option.is_some() {
|
||||||
@ -158,7 +155,7 @@ pub async fn filter_enlisted_projects_ids(
|
|||||||
)
|
)
|
||||||
.fetch(pool)
|
.fetch(pool)
|
||||||
.map_ok(|row| {
|
.map_ok(|row| {
|
||||||
for x in projects.iter() {
|
for x in &projects {
|
||||||
let bool =
|
let bool =
|
||||||
Some(x.id.0) == row.id && Some(x.team_id.0) == row.team_id;
|
Some(x.id.0) == row.id && Some(x.team_id.0) == row.team_id;
|
||||||
if bool {
|
if bool {
|
||||||
@ -238,7 +235,6 @@ pub async fn filter_visible_version_ids(
|
|||||||
redis: &RedisPool,
|
redis: &RedisPool,
|
||||||
) -> Result<Vec<crate::database::models::DBVersionId>, ApiError> {
|
) -> Result<Vec<crate::database::models::DBVersionId>, ApiError> {
|
||||||
let mut return_versions = Vec::new();
|
let mut return_versions = Vec::new();
|
||||||
let mut check_versions = Vec::new();
|
|
||||||
|
|
||||||
// First, filter out versions belonging to projects we can't see
|
// First, filter out versions belonging to projects we can't see
|
||||||
// (ie: a hidden project, but public version, should still be hidden)
|
// (ie: a hidden project, but public version, should still be hidden)
|
||||||
@ -271,15 +267,10 @@ pub async fn filter_visible_version_ids(
|
|||||||
// - we are enlisted on the team of the mod
|
// - we are enlisted on the team of the mod
|
||||||
if (!version.status.is_hidden()
|
if (!version.status.is_hidden()
|
||||||
&& visible_project_ids.contains(&version.project_id))
|
&& visible_project_ids.contains(&version.project_id))
|
||||||
|| user_option
|
|| user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.role.is_mod())
|
|
||||||
.unwrap_or(false)
|
|
||||||
|| enlisted_version_ids.contains(&version.id)
|
|| enlisted_version_ids.contains(&version.id)
|
||||||
{
|
{
|
||||||
return_versions.push(version.id);
|
return_versions.push(version.id);
|
||||||
} else if user_option.is_some() {
|
|
||||||
check_versions.push(version);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,10 +301,7 @@ pub async fn filter_enlisted_version_ids(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for version in versions {
|
for version in versions {
|
||||||
if user_option
|
if user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.role.is_mod())
|
|
||||||
.unwrap_or(false)
|
|
||||||
|| (user_option.is_some()
|
|| (user_option.is_some()
|
||||||
&& authorized_project_ids.contains(&version.project_id))
|
&& authorized_project_ids.contains(&version.project_id))
|
||||||
{
|
{
|
||||||
@ -349,10 +337,7 @@ pub async fn filter_visible_collections(
|
|||||||
|
|
||||||
for collection in collections {
|
for collection in collections {
|
||||||
if !collection.status.is_hidden()
|
if !collection.status.is_hidden()
|
||||||
|| user_option
|
|| user_option.as_ref().is_some_and(|x| x.role.is_mod())
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.role.is_mod())
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
return_collections.push(collection.into());
|
return_collections.push(collection.into());
|
||||||
} else if user_option.is_some() {
|
} else if user_option.is_some() {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use crate::auth::get_user_from_headers;
|
use crate::auth::get_user_from_headers;
|
||||||
use crate::auth::oauth::uris::{OAuthRedirectUris, ValidatedRedirectUri};
|
use crate::auth::oauth::uris::{OAuthRedirectUris, ValidatedRedirectUri};
|
||||||
use crate::auth::validate::extract_authorization_header;
|
use crate::auth::validate::extract_authorization_header;
|
||||||
@ -17,7 +19,7 @@ use crate::queue::session::AuthQueue;
|
|||||||
use actix_web::http::header::{CACHE_CONTROL, LOCATION, PRAGMA};
|
use actix_web::http::header::{CACHE_CONTROL, LOCATION, PRAGMA};
|
||||||
use actix_web::web::{Data, Query, ServiceConfig};
|
use actix_web::web::{Data, Query, ServiceConfig};
|
||||||
use actix_web::{HttpRequest, HttpResponse, get, post, web};
|
use actix_web::{HttpRequest, HttpResponse, get, post, web};
|
||||||
use chrono::Duration;
|
use chrono::{DateTime, Duration};
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use rand_chacha::ChaCha20Rng;
|
use rand_chacha::ChaCha20Rng;
|
||||||
@ -280,8 +282,8 @@ pub async fn request_token(
|
|||||||
authorization_id,
|
authorization_id,
|
||||||
token_hash,
|
token_hash,
|
||||||
scopes,
|
scopes,
|
||||||
created: Default::default(),
|
created: DateTime::default(),
|
||||||
expires: Default::default(),
|
expires: DateTime::default(),
|
||||||
last_used: None,
|
last_used: None,
|
||||||
client_id,
|
client_id,
|
||||||
user_id,
|
user_id,
|
||||||
@ -456,7 +458,7 @@ fn append_params_to_uri(uri: &str, params: &[impl AsRef<str>]) -> String {
|
|||||||
let mut uri = uri.to_string();
|
let mut uri = uri.to_string();
|
||||||
let mut connector = if uri.contains('?') { "&" } else { "?" };
|
let mut connector = if uri.contains('?') { "&" } else { "?" };
|
||||||
for param in params {
|
for param in params {
|
||||||
uri.push_str(&format!("{}{}", connector, param.as_ref()));
|
write!(&mut uri, "{connector}{}", param.as_ref()).unwrap();
|
||||||
connector = "&";
|
connector = "&";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -93,12 +93,11 @@ where
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let rate_limit_ignore = dotenvy::var("RATE_LIMIT_IGNORE_KEY")?;
|
let rate_limit_ignore = dotenvy::var("RATE_LIMIT_IGNORE_KEY")?;
|
||||||
if !req
|
if req
|
||||||
.headers()
|
.headers()
|
||||||
.get("x-ratelimit-key")
|
.get("x-ratelimit-key")
|
||||||
.and_then(|x| x.to_str().ok())
|
.and_then(|x| x.to_str().ok())
|
||||||
.map(|x| x == rate_limit_ignore)
|
.is_none_or(|x| x != rate_limit_ignore)
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
let metadata = get_session_metadata(req).await?;
|
let metadata = get_session_metadata(req).await?;
|
||||||
session_queue.add_session(session.id, metadata).await;
|
session_queue.add_session(session.id, metadata).await;
|
||||||
@ -130,7 +129,7 @@ where
|
|||||||
|
|
||||||
user.map(|u| (access_token.scopes, u))
|
user.map(|u| (access_token.scopes, u))
|
||||||
}
|
}
|
||||||
Some(("github", _)) | Some(("gho", _)) | Some(("ghp", _)) => {
|
Some(("github" | "gho" | "ghp", _)) => {
|
||||||
let user = AuthProvider::GitHub.get_user(token).await?;
|
let user = AuthProvider::GitHub.get_user(token).await?;
|
||||||
let id =
|
let id =
|
||||||
AuthProvider::GitHub.get_user_id(&user.id, executor).await?;
|
AuthProvider::GitHub.get_user_id(&user.id, executor).await?;
|
||||||
|
|||||||
@ -105,7 +105,7 @@ impl DBFriend {
|
|||||||
created: row.created,
|
created: row.created,
|
||||||
accepted: row.accepted,
|
accepted: row.accepted,
|
||||||
})
|
})
|
||||||
.filter(|x| accepted.map(|y| y == x.accepted).unwrap_or(true))
|
.filter(|x| accepted.is_none_or(|y| y == x.accepted))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(friends)
|
Ok(friends)
|
||||||
|
|||||||
@ -675,7 +675,7 @@ impl LoaderFieldEnumValue {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
let mut bool = true;
|
let mut bool = true;
|
||||||
for (key, value) in filter.iter() {
|
for (key, value) in &filter {
|
||||||
if let Some(metadata_value) = x.metadata.get(key) {
|
if let Some(metadata_value) = x.metadata.get(key) {
|
||||||
bool &= metadata_value == value;
|
bool &= metadata_value == value;
|
||||||
} else {
|
} else {
|
||||||
@ -713,7 +713,7 @@ impl VersionField {
|
|||||||
query_version_fields.push(base.clone().with_string_value(s))
|
query_version_fields.push(base.clone().with_string_value(s))
|
||||||
}
|
}
|
||||||
VersionFieldValue::Boolean(b) => query_version_fields
|
VersionFieldValue::Boolean(b) => query_version_fields
|
||||||
.push(base.clone().with_int_value(if b { 1 } else { 0 })),
|
.push(base.clone().with_int_value(b as i32)),
|
||||||
VersionFieldValue::ArrayInteger(v) => {
|
VersionFieldValue::ArrayInteger(v) => {
|
||||||
for i in v {
|
for i in v {
|
||||||
query_version_fields
|
query_version_fields
|
||||||
@ -728,9 +728,8 @@ impl VersionField {
|
|||||||
}
|
}
|
||||||
VersionFieldValue::ArrayBoolean(v) => {
|
VersionFieldValue::ArrayBoolean(v) => {
|
||||||
for b in v {
|
for b in v {
|
||||||
query_version_fields.push(
|
query_version_fields
|
||||||
base.clone().with_int_value(if b { 1 } else { 0 }),
|
.push(base.clone().with_int_value(b as i32));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VersionFieldValue::Enum(_, v) => query_version_fields
|
VersionFieldValue::Enum(_, v) => query_version_fields
|
||||||
@ -757,7 +756,7 @@ impl VersionField {
|
|||||||
l.field_id.0,
|
l.field_id.0,
|
||||||
l.version_id.0,
|
l.version_id.0,
|
||||||
l.int_value,
|
l.int_value,
|
||||||
l.enum_value.as_ref().map(|e| e.0).unwrap_or(-1),
|
l.enum_value.as_ref().map_or(-1, |e| e.0),
|
||||||
l.string_value.clone(),
|
l.string_value.clone(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -849,12 +848,11 @@ impl VersionField {
|
|||||||
query_loader_fields
|
query_loader_fields
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|q| {
|
.flat_map(|q| {
|
||||||
let loader_field_type = match LoaderFieldType::build(
|
let Some(loader_field_type) = LoaderFieldType::build(
|
||||||
&q.field_type,
|
&q.field_type,
|
||||||
q.enum_type.map(|l| l.0),
|
q.enum_type.map(|l| l.0),
|
||||||
) {
|
) else {
|
||||||
Some(lft) => lft,
|
return vec![];
|
||||||
None => return vec![],
|
|
||||||
};
|
};
|
||||||
let loader_field = LoaderField {
|
let loader_field = LoaderField {
|
||||||
id: q.id,
|
id: q.id,
|
||||||
@ -1085,23 +1083,17 @@ impl VersionFieldValue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check errors- version_id must all be the same
|
// Check errors- version_id must all be the same
|
||||||
|
// If the field type is a non-array, then the reason for multiple version ids is that there are multiple versions being aggregated, and those version ids are contained within.
|
||||||
|
// If the field type is an array, then the reason for multiple version ids is that there are multiple values for a single version
|
||||||
|
// (or a greater aggregation between multiple arrays, in which case the per-field version is lost, so we just take the first one and use it for that)
|
||||||
let version_id = qvfs
|
let version_id = qvfs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|qvf| qvf.version_id)
|
.map(|qvf| qvf.version_id)
|
||||||
.unique()
|
.unique()
|
||||||
.collect::<Vec<_>>();
|
.next()
|
||||||
// If the field type is a non-array, then the reason for multiple version ids is that there are multiple versions being aggregated, and those version ids are contained within.
|
.unwrap_or(DBVersionId(0));
|
||||||
// If the field type is an array, then the reason for multiple version ids is that there are multiple values for a single version
|
|
||||||
// (or a greater aggregation between multiple arrays, in which case the per-field version is lost, so we just take the first one and use it for that)
|
|
||||||
let version_id =
|
|
||||||
version_id.into_iter().next().unwrap_or(DBVersionId(0));
|
|
||||||
|
|
||||||
let field_id = qvfs
|
if qvfs.iter().map(|qvf| qvf.field_id).unique().count() > 1 {
|
||||||
.iter()
|
|
||||||
.map(|qvf| qvf.field_id)
|
|
||||||
.unique()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if field_id.len() > 1 {
|
|
||||||
return Err(DatabaseError::SchemaError(format!(
|
return Err(DatabaseError::SchemaError(format!(
|
||||||
"Multiple field ids for field {field_name}"
|
"Multiple field ids for field {field_name}"
|
||||||
)));
|
)));
|
||||||
@ -1274,7 +1266,7 @@ impl VersionFieldValue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Sort arrayenums by ordering, then by created
|
// Sort arrayenums by ordering, then by created
|
||||||
for (_, v) in value.iter_mut() {
|
for (_, v) in &mut value {
|
||||||
if let VersionFieldValue::ArrayEnum(_, v) = v {
|
if let VersionFieldValue::ArrayEnum(_, v) = v {
|
||||||
v.sort_by(|a, b| {
|
v.sort_by(|a, b| {
|
||||||
a.ordering.cmp(&b.ordering).then(a.created.cmp(&b.created))
|
a.ordering.cmp(&b.ordering).then(a.created.cmp(&b.created))
|
||||||
@ -1317,8 +1309,8 @@ impl VersionFieldValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For conversion to an interanl string(s), such as for search facets, filtering, or direct hardcoding
|
// For conversion to an internal string(s), such as for search facets, filtering, or direct hardcoding
|
||||||
// No matter the type, it will be converted to a Vec<String>, whre the non-array types will have a single element
|
// No matter the type, it will be converted to a Vec<String>, where the non-array types will have a single element
|
||||||
pub fn as_strings(&self) -> Vec<String> {
|
pub fn as_strings(&self) -> Vec<String> {
|
||||||
match self {
|
match self {
|
||||||
VersionFieldValue::Integer(i) => vec![i.to_string()],
|
VersionFieldValue::Integer(i) => vec![i.to_string()],
|
||||||
@ -1343,22 +1335,19 @@ impl VersionFieldValue {
|
|||||||
VersionFieldValue::Integer(i) => value.as_i64() == Some(*i as i64),
|
VersionFieldValue::Integer(i) => value.as_i64() == Some(*i as i64),
|
||||||
VersionFieldValue::Text(s) => value.as_str() == Some(s),
|
VersionFieldValue::Text(s) => value.as_str() == Some(s),
|
||||||
VersionFieldValue::Boolean(b) => value.as_bool() == Some(*b),
|
VersionFieldValue::Boolean(b) => value.as_bool() == Some(*b),
|
||||||
VersionFieldValue::ArrayInteger(v) => value
|
VersionFieldValue::ArrayInteger(v) => {
|
||||||
.as_i64()
|
value.as_i64().is_some_and(|i| v.contains(&(i as i32)))
|
||||||
.map(|i| v.contains(&(i as i32)))
|
}
|
||||||
.unwrap_or(false),
|
VersionFieldValue::ArrayText(v) => {
|
||||||
VersionFieldValue::ArrayText(v) => value
|
value.as_str().is_some_and(|s| v.contains(&s.to_string()))
|
||||||
.as_str()
|
}
|
||||||
.map(|s| v.contains(&s.to_string()))
|
|
||||||
.unwrap_or(false),
|
|
||||||
VersionFieldValue::ArrayBoolean(v) => {
|
VersionFieldValue::ArrayBoolean(v) => {
|
||||||
value.as_bool().map(|b| v.contains(&b)).unwrap_or(false)
|
value.as_bool().is_some_and(|b| v.contains(&b))
|
||||||
}
|
}
|
||||||
VersionFieldValue::Enum(_, v) => value.as_str() == Some(&v.value),
|
VersionFieldValue::Enum(_, v) => value.as_str() == Some(&v.value),
|
||||||
VersionFieldValue::ArrayEnum(_, v) => value
|
VersionFieldValue::ArrayEnum(_, v) => value
|
||||||
.as_str()
|
.as_str()
|
||||||
.map(|s| v.iter().any(|v| v.value == s))
|
.is_some_and(|s| v.iter().any(|v| v.value == s)),
|
||||||
.unwrap_or(false),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,7 +122,7 @@ impl DBOrganization {
|
|||||||
|ids| async move {
|
|ids| async move {
|
||||||
let org_ids: Vec<i64> = ids
|
let org_ids: Vec<i64> = ids
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||||
.map(|x| x as i64)
|
.map(|x| x as i64)
|
||||||
.collect();
|
.collect();
|
||||||
let slugs = ids
|
let slugs = ids
|
||||||
|
|||||||
@ -108,7 +108,7 @@ impl DBPersonalAccessToken {
|
|||||||
|ids| async move {
|
|ids| async move {
|
||||||
let pat_ids: Vec<i64> = ids
|
let pat_ids: Vec<i64> = ids
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||||
.map(|x| x as i64)
|
.map(|x| x as i64)
|
||||||
.collect();
|
.collect();
|
||||||
let slugs = ids.into_iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
let slugs = ids.into_iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
||||||
|
|||||||
@ -545,7 +545,7 @@ impl DBProject {
|
|||||||
let mut exec = exec.acquire().await?;
|
let mut exec = exec.acquire().await?;
|
||||||
let project_ids_parsed: Vec<i64> = ids
|
let project_ids_parsed: Vec<i64> = ids
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||||
.map(|x| x as i64)
|
.map(|x| x as i64)
|
||||||
.collect();
|
.collect();
|
||||||
let slugs = ids
|
let slugs = ids
|
||||||
@ -723,7 +723,7 @@ impl DBProject {
|
|||||||
|
|
||||||
// Add loader fields to the set we need to fetch
|
// Add loader fields to the set we need to fetch
|
||||||
let loader_loader_field_ids = m.loader_fields.unwrap_or_default().into_iter().map(LoaderFieldId).collect::<Vec<_>>();
|
let loader_loader_field_ids = m.loader_fields.unwrap_or_default().into_iter().map(LoaderFieldId).collect::<Vec<_>>();
|
||||||
for loader_field_id in loader_loader_field_ids.iter() {
|
for loader_field_id in &loader_loader_field_ids {
|
||||||
loader_field_ids.insert(*loader_field_id);
|
loader_field_ids.insert(*loader_field_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -153,7 +153,7 @@ impl DBSession {
|
|||||||
|ids| async move {
|
|ids| async move {
|
||||||
let session_ids: Vec<i64> = ids
|
let session_ids: Vec<i64> = ids
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||||
.map(|x| x as i64)
|
.map(|x| x as i64)
|
||||||
.collect();
|
.collect();
|
||||||
let slugs = ids
|
let slugs = ids
|
||||||
|
|||||||
@ -45,7 +45,7 @@ impl TeamBuilder {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut team_member_ids = Vec::new();
|
let mut team_member_ids = Vec::new();
|
||||||
for _ in self.members.iter() {
|
for _ in &self.members {
|
||||||
team_member_ids.push(generate_team_member_id(transaction).await?.0);
|
team_member_ids.push(generate_team_member_id(transaction).await?.0);
|
||||||
}
|
}
|
||||||
let TeamBuilder { members } = self;
|
let TeamBuilder { members } = self;
|
||||||
|
|||||||
@ -163,7 +163,7 @@ impl DBUser {
|
|||||||
|ids| async move {
|
|ids| async move {
|
||||||
let user_ids: Vec<i64> = ids
|
let user_ids: Vec<i64> = ids
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|x| parse_base62(&x.to_string()).ok())
|
.filter_map(|x| parse_base62(&x.to_string()).ok())
|
||||||
.map(|x| x as i64)
|
.map(|x| x as i64)
|
||||||
.collect();
|
.collect();
|
||||||
let slugs = ids
|
let slugs = ids
|
||||||
|
|||||||
@ -52,7 +52,7 @@ impl DependencyBuilder {
|
|||||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<(), DatabaseError> {
|
) -> Result<(), DatabaseError> {
|
||||||
let mut project_ids = Vec::new();
|
let mut project_ids = Vec::new();
|
||||||
for dependency in builders.iter() {
|
for dependency in &builders {
|
||||||
project_ids.push(
|
project_ids.push(
|
||||||
dependency
|
dependency
|
||||||
.try_get_project_id(transaction)
|
.try_get_project_id(transaction)
|
||||||
@ -333,9 +333,7 @@ impl DBVersion {
|
|||||||
) -> Result<Option<()>, DatabaseError> {
|
) -> Result<Option<()>, DatabaseError> {
|
||||||
let result = Self::get(id, &mut **transaction, redis).await?;
|
let result = Self::get(id, &mut **transaction, redis).await?;
|
||||||
|
|
||||||
let result = if let Some(result) = result {
|
let Some(result) = result else {
|
||||||
result
|
|
||||||
} else {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -550,7 +548,7 @@ impl DBVersion {
|
|||||||
|
|
||||||
// Add loader fields to the set we need to fetch
|
// Add loader fields to the set we need to fetch
|
||||||
let loader_loader_field_ids = m.loader_fields.unwrap_or_default().into_iter().map(LoaderFieldId).collect::<Vec<_>>();
|
let loader_loader_field_ids = m.loader_fields.unwrap_or_default().into_iter().map(LoaderFieldId).collect::<Vec<_>>();
|
||||||
for loader_field_id in loader_loader_field_ids.iter() {
|
for loader_field_id in &loader_loader_field_ids {
|
||||||
loader_field_ids.insert(*loader_field_id);
|
loader_field_ids.insert(*loader_field_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,7 +754,7 @@ impl DBVersion {
|
|||||||
let mut files = files.into_iter().map(|x| {
|
let mut files = files.into_iter().map(|x| {
|
||||||
let mut file_hashes = HashMap::new();
|
let mut file_hashes = HashMap::new();
|
||||||
|
|
||||||
for hash in hashes.iter() {
|
for hash in &hashes {
|
||||||
if hash.file_id == x.id {
|
if hash.file_id == x.id {
|
||||||
file_hashes.insert(
|
file_hashes.insert(
|
||||||
hash.algorithm.clone(),
|
hash.algorithm.clone(),
|
||||||
@ -852,7 +850,7 @@ impl DBVersion {
|
|||||||
ORDER BY v.date_published
|
ORDER BY v.date_published
|
||||||
",
|
",
|
||||||
algorithm,
|
algorithm,
|
||||||
&file_ids.into_iter().flat_map(|x| x.split('_').last().map(|x| x.as_bytes().to_vec())).collect::<Vec<_>>(),
|
&file_ids.into_iter().filter_map(|x| x.split('_').last().map(|x| x.as_bytes().to_vec())).collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.fetch(executor)
|
.fetch(executor)
|
||||||
.try_fold(DashMap::new(), |acc, f| {
|
.try_fold(DashMap::new(), |acc, f| {
|
||||||
@ -1042,14 +1040,14 @@ mod tests {
|
|||||||
date_published,
|
date_published,
|
||||||
project_id: DBProjectId(0),
|
project_id: DBProjectId(0),
|
||||||
author_id: DBUserId(0),
|
author_id: DBUserId(0),
|
||||||
name: Default::default(),
|
name: String::new(),
|
||||||
version_number: Default::default(),
|
version_number: String::new(),
|
||||||
changelog: Default::default(),
|
changelog: String::new(),
|
||||||
downloads: Default::default(),
|
downloads: 0,
|
||||||
version_type: Default::default(),
|
version_type: String::new(),
|
||||||
featured: Default::default(),
|
featured: false,
|
||||||
status: VersionStatus::Listed,
|
status: VersionStatus::Listed,
|
||||||
requested_status: Default::default(),
|
requested_status: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use ariadne::ids::base62_impl::{parse_base62, to_base62};
|
|||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use deadpool_redis::{Config, Runtime};
|
use deadpool_redis::{Config, Runtime};
|
||||||
|
use futures::future::Either;
|
||||||
use prometheus::{IntGauge, Registry};
|
use prometheus::{IntGauge, Registry};
|
||||||
use redis::{Cmd, ExistenceCheck, SetExpiry, SetOptions, cmd};
|
use redis::{Cmd, ExistenceCheck, SetExpiry, SetOptions, cmd};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
@ -11,7 +12,6 @@ use std::collections::HashMap;
|
|||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
const DEFAULT_EXPIRY: i64 = 60 * 60 * 12; // 12 hours
|
const DEFAULT_EXPIRY: i64 = 60 * 60 * 12; // 12 hours
|
||||||
@ -378,22 +378,10 @@ impl RedisPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
let mut fetch_tasks = Vec::new();
|
||||||
let mut fetch_tasks: Vec<
|
|
||||||
Pin<
|
|
||||||
Box<
|
|
||||||
dyn Future<
|
|
||||||
Output = Result<
|
|
||||||
HashMap<K, RedisValue<T, K, S>>,
|
|
||||||
DatabaseError,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
> = Vec::new();
|
|
||||||
|
|
||||||
if !ids.is_empty() {
|
if !ids.is_empty() {
|
||||||
fetch_tasks.push(Box::pin(async {
|
fetch_tasks.push(Either::Left(async {
|
||||||
let fetch_ids =
|
let fetch_ids =
|
||||||
ids.iter().map(|x| x.value().clone()).collect::<Vec<_>>();
|
ids.iter().map(|x| x.value().clone()).collect::<Vec<_>>();
|
||||||
|
|
||||||
@ -491,7 +479,7 @@ impl RedisPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !subscribe_ids.is_empty() {
|
if !subscribe_ids.is_empty() {
|
||||||
fetch_tasks.push(Box::pin(async {
|
fetch_tasks.push(Either::Right(async {
|
||||||
let mut interval =
|
let mut interval =
|
||||||
tokio::time::interval(Duration::from_millis(100));
|
tokio::time::interval(Duration::from_millis(100));
|
||||||
let start = Utc::now();
|
let start = Utc::now();
|
||||||
|
|||||||
@ -313,13 +313,16 @@ pub fn app_config(
|
|||||||
.app_data(labrinth_config.automated_moderation_queue.clone())
|
.app_data(labrinth_config.automated_moderation_queue.clone())
|
||||||
.app_data(web::Data::new(labrinth_config.stripe_client.clone()))
|
.app_data(web::Data::new(labrinth_config.stripe_client.clone()))
|
||||||
.app_data(labrinth_config.rate_limiter.clone())
|
.app_data(labrinth_config.rate_limiter.clone())
|
||||||
.configure(
|
.configure({
|
||||||
#[allow(unused_variables)]
|
|
||||||
|cfg| {
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
routes::debug::config(cfg)
|
{
|
||||||
},
|
|cfg| routes::debug::config(cfg)
|
||||||
)
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
|_cfg| ()
|
||||||
|
}
|
||||||
|
})
|
||||||
.configure(routes::v2::config)
|
.configure(routes::v2::config)
|
||||||
.configure(routes::v3::config)
|
.configure(routes::v3::config)
|
||||||
.configure(routes::internal::config)
|
.configure(routes::internal::config)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use labrinth::file_hosting::S3Host;
|
|||||||
use labrinth::search;
|
use labrinth::search;
|
||||||
use labrinth::util::ratelimit::rate_limit_middleware;
|
use labrinth::util::ratelimit::rate_limit_middleware;
|
||||||
use labrinth::{check_env_vars, clickhouse, database, file_hosting, queue};
|
use labrinth::{check_env_vars, clickhouse, database, file_hosting, queue};
|
||||||
|
use std::ffi::CStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
@ -16,10 +17,8 @@ use tracing_actix_web::TracingLogger;
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||||
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
#[unsafe(export_name = "malloc_conf")]
|
#[unsafe(export_name = "malloc_conf")]
|
||||||
pub static malloc_conf: &[u8] =
|
pub static MALLOC_CONF: &CStr = c"prof:true,prof_active:true,lg_prof_sample:19";
|
||||||
b"prof:true,prof_active:true,lg_prof_sample:19\0";
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Pepper {
|
pub struct Pepper {
|
||||||
|
|||||||
@ -122,7 +122,7 @@ pub fn from_duplicate_version_fields(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove duplicates
|
// Remove duplicates
|
||||||
for (_, v) in fields.iter_mut() {
|
for v in fields.values_mut() {
|
||||||
*v = mem::take(v).into_iter().unique().collect_vec();
|
*v = mem::take(v).into_iter().unique().collect_vec();
|
||||||
}
|
}
|
||||||
fields
|
fields
|
||||||
|
|||||||
@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sha1::Digest;
|
use sha1::Digest;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::io::{Cursor, Read};
|
use std::io::{Cursor, Read};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
@ -33,29 +34,31 @@ impl ModerationMessages {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn markdown(&self, auto_mod: bool) -> String {
|
pub fn markdown(&self, auto_mod: bool) -> String {
|
||||||
let mut str = "".to_string();
|
let mut str = String::new();
|
||||||
|
|
||||||
for message in &self.messages {
|
for message in &self.messages {
|
||||||
str.push_str(&format!("## {}\n", message.header()));
|
write!(&mut str, "## {}\n{}\n\n", message.header(), message.body())
|
||||||
str.push_str(&format!("{}\n", message.body()));
|
.unwrap();
|
||||||
str.push('\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (version_num, messages) in &self.version_specific {
|
for (version_num, messages) in &self.version_specific {
|
||||||
for message in messages {
|
for message in messages {
|
||||||
str.push_str(&format!(
|
write!(
|
||||||
"## Version {}: {}\n",
|
&mut str,
|
||||||
|
"### Version {}: {}\n{}\n\n",
|
||||||
version_num,
|
version_num,
|
||||||
message.header()
|
message.header(),
|
||||||
));
|
message.body()
|
||||||
str.push_str(&format!("{}\n", message.body()));
|
)
|
||||||
str.push('\n');
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if auto_mod {
|
if auto_mod {
|
||||||
str.push_str("<hr />\n\n");
|
str.push_str(
|
||||||
str.push_str("🤖 This is an automated message generated by AutoMod (BETA). If you are facing issues, please [contact support](https://support.modrinth.com).");
|
"<hr />\n\n\
|
||||||
|
🤖 This is an automated message generated by AutoMod (BETA). If you are facing issues, please [contact support](https://support.modrinth.com)."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
str
|
str
|
||||||
@ -146,14 +149,13 @@ impl ModerationMessage {
|
|||||||
match self {
|
match self {
|
||||||
ModerationMessage::NoPrimaryFile => "Please attach a file to this version. All files on Modrinth must have files associated with their versions.\n".to_string(),
|
ModerationMessage::NoPrimaryFile => "Please attach a file to this version. All files on Modrinth must have files associated with their versions.\n".to_string(),
|
||||||
ModerationMessage::PackFilesNotAllowed { files, .. } => {
|
ModerationMessage::PackFilesNotAllowed { files, .. } => {
|
||||||
let mut str = "".to_string();
|
let mut str = String::from("This pack redistributes copyrighted material. Please refer to [Modrinth's guide on obtaining modpack permissions](https://support.modrinth.com/en/articles/8797527-obtaining-modpack-permissions) for more information.\n\n");
|
||||||
str.push_str("This pack redistributes copyrighted material. Please refer to [Modrinth's guide on obtaining modpack permissions](https://support.modrinth.com/en/articles/8797527-obtaining-modpack-permissions) for more information.\n\n");
|
|
||||||
|
|
||||||
let mut attribute_mods = Vec::new();
|
let mut attribute_mods = Vec::new();
|
||||||
let mut no_mods = Vec::new();
|
let mut no_mods = Vec::new();
|
||||||
let mut permanent_no_mods = Vec::new();
|
let mut permanent_no_mods = Vec::new();
|
||||||
let mut unidentified_mods = Vec::new();
|
let mut unidentified_mods = Vec::new();
|
||||||
for (_, approval) in files.iter() {
|
for approval in files.values() {
|
||||||
match approval.status {
|
match approval.status {
|
||||||
ApprovalType::Yes | ApprovalType::WithAttributionAndSource => {}
|
ApprovalType::Yes | ApprovalType::WithAttributionAndSource => {}
|
||||||
ApprovalType::WithAttribution => attribute_mods.push(&approval.file_name),
|
ApprovalType::WithAttribution => attribute_mods.push(&approval.file_name),
|
||||||
@ -166,7 +168,7 @@ impl ModerationMessage {
|
|||||||
fn print_mods(projects: Vec<&String>, headline: &str, val: &mut String) {
|
fn print_mods(projects: Vec<&String>, headline: &str, val: &mut String) {
|
||||||
if projects.is_empty() { return }
|
if projects.is_empty() { return }
|
||||||
|
|
||||||
val.push_str(&format!("{headline}\n\n"));
|
write!(val, "{headline}\n\n").unwrap();
|
||||||
|
|
||||||
for project in &projects {
|
for project in &projects {
|
||||||
let additional_text = if project.contains("ftb-quests") {
|
let additional_text = if project.contains("ftb-quests") {
|
||||||
@ -181,11 +183,11 @@ impl ModerationMessage {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
val.push_str(&if let Some(additional_text) = additional_text {
|
if let Some(additional_text) = additional_text {
|
||||||
format!("- {project} (consider using [{}](https://modrinth.com/project/{}) instead)\n", additional_text.0, additional_text.1)
|
writeln!(val, "- {project} (consider using [{}](https://modrinth.com/project/{}) instead)", additional_text.0, additional_text.1).unwrap();
|
||||||
} else {
|
} else {
|
||||||
format!("- {project}\n")
|
writeln!(val, "- {project}").unwrap();
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !projects.is_empty() {
|
if !projects.is_empty() {
|
||||||
@ -278,10 +280,7 @@ impl AutomatedModerationQueue {
|
|||||||
let mut zip = ZipArchive::new(reader)?;
|
let mut zip = ZipArchive::new(reader)?;
|
||||||
|
|
||||||
let pack: PackFormat = {
|
let pack: PackFormat = {
|
||||||
let mut file =
|
let Ok(mut file) = zip.by_name("modrinth.index.json") else {
|
||||||
if let Ok(file) = zip.by_name("modrinth.index.json") {
|
|
||||||
file
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -301,7 +300,7 @@ impl AutomatedModerationQueue {
|
|||||||
.files
|
.files
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|x| {
|
.filter_map(|x| {
|
||||||
let hash = x.hashes.get(&PackFileHash::Sha1);
|
let hash = x.hashes.get(&PackFileHash::Sha1);
|
||||||
|
|
||||||
if let Some(hash) = hash {
|
if let Some(hash) = hash {
|
||||||
@ -397,8 +396,8 @@ impl AutomatedModerationQueue {
|
|||||||
",
|
",
|
||||||
serde_json::to_value(&MissingMetadata {
|
serde_json::to_value(&MissingMetadata {
|
||||||
identified: final_hashes,
|
identified: final_hashes,
|
||||||
flame_files: Default::default(),
|
flame_files: HashMap::new(),
|
||||||
unknown_files: Default::default(),
|
unknown_files: HashMap::new(),
|
||||||
})?,
|
})?,
|
||||||
primary_file.id.0
|
primary_file.id.0
|
||||||
)
|
)
|
||||||
@ -432,8 +431,8 @@ impl AutomatedModerationQueue {
|
|||||||
if hashes.is_empty() {
|
if hashes.is_empty() {
|
||||||
let metadata = MissingMetadata {
|
let metadata = MissingMetadata {
|
||||||
identified: final_hashes,
|
identified: final_hashes,
|
||||||
flame_files: Default::default(),
|
flame_files: HashMap::new(),
|
||||||
unknown_files: Default::default(),
|
unknown_files: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
@ -533,8 +532,8 @@ impl AutomatedModerationQueue {
|
|||||||
if hashes.is_empty() {
|
if hashes.is_empty() {
|
||||||
let metadata = MissingMetadata {
|
let metadata = MissingMetadata {
|
||||||
identified: final_hashes,
|
identified: final_hashes,
|
||||||
flame_files: Default::default(),
|
flame_files: HashMap::new(),
|
||||||
unknown_files: Default::default(),
|
unknown_files: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
@ -622,8 +621,7 @@ impl AutomatedModerationQueue {
|
|||||||
|
|
||||||
if !mod_messages.is_empty() {
|
if !mod_messages.is_empty() {
|
||||||
let first_time = database::models::DBThread::get(project.thread_id, &pool).await?
|
let first_time = database::models::DBThread::get(project.thread_id, &pool).await?
|
||||||
.map(|x| x.messages.iter().all(|x| x.author_id == Some(database::models::DBUserId(AUTOMOD_ID)) || x.hide_identity))
|
.is_none_or(|x| x.messages.iter().all(|x| x.author_id == Some(database::models::DBUserId(AUTOMOD_ID)) || x.hide_identity));
|
||||||
.unwrap_or(true);
|
|
||||||
|
|
||||||
let mut transaction = pool.begin().await?;
|
let mut transaction = pool.begin().await?;
|
||||||
let id = ThreadMessageBuilder {
|
let id = ThreadMessageBuilder {
|
||||||
@ -733,10 +731,12 @@ impl AutomatedModerationQueue {
|
|||||||
if let Err(err) = res {
|
if let Err(err) = res {
|
||||||
let err = err.as_api_error();
|
let err = err.as_api_error();
|
||||||
|
|
||||||
let mut str = String::new();
|
let str = format!(
|
||||||
str.push_str("## Internal AutoMod Error\n\n");
|
"## Internal AutoMod Error\n\n\
|
||||||
str.push_str(&format!("Error code: {}\n\n", err.error));
|
Error code: {}\n\n\
|
||||||
str.push_str(&format!("Error description: {}\n\n", err.description));
|
Error description: {}\n\n",
|
||||||
|
err.error, err.description
|
||||||
|
);
|
||||||
|
|
||||||
let mut transaction = pool.begin().await?;
|
let mut transaction = pool.begin().await?;
|
||||||
ThreadMessageBuilder {
|
ThreadMessageBuilder {
|
||||||
|
|||||||
@ -441,8 +441,8 @@ impl PayoutsQueue {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PayoutMethodFee {
|
PayoutMethodFee {
|
||||||
percentage: Default::default(),
|
percentage: Decimal::default(),
|
||||||
min: Default::default(),
|
min: Decimal::default(),
|
||||||
max: None,
|
max: None,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -833,7 +833,7 @@ pub async fn process_payout(
|
|||||||
.map(|x| (x.project_id, x.page_views))
|
.map(|x| (x.project_id, x.page_views))
|
||||||
.collect::<HashMap<u64, u64>>();
|
.collect::<HashMap<u64, u64>>();
|
||||||
|
|
||||||
for (key, value) in downloads_values.iter() {
|
for (key, value) in &downloads_values {
|
||||||
let counter = views_values.entry(*key).or_insert(0);
|
let counter = views_values.entry(*key).or_insert(0);
|
||||||
*counter += *value;
|
*counter += *value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -216,7 +216,7 @@ pub async fn playtime_ingest(
|
|||||||
version_id: version.inner.id.0 as u64,
|
version_id: version.inner.id.0 as u64,
|
||||||
loader: playtime.loader,
|
loader: playtime.loader,
|
||||||
game_version: playtime.game_version,
|
game_version: playtime.game_version,
|
||||||
parent: playtime.parent.map(|x| x.0).unwrap_or(0),
|
parent: playtime.parent.map_or(0, |x| x.0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ use actix_web::{HttpRequest, HttpResponse, get, patch, post, web};
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@ -221,9 +222,11 @@ pub async fn delphi_result_ingest(
|
|||||||
|
|
||||||
for (issue, trace) in &body.issues {
|
for (issue, trace) in &body.issues {
|
||||||
for (path, code) in trace {
|
for (path, code) in trace {
|
||||||
header.push_str(&format!(
|
write!(
|
||||||
|
&mut header,
|
||||||
"\n issue {issue} found at file {path}: \n ```\n{code}\n```"
|
"\n issue {issue} found at file {path}: \n ```\n{code}\n```"
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,12 +247,15 @@ pub async fn delphi_result_ingest(
|
|||||||
|
|
||||||
for (issue, trace) in &body.issues {
|
for (issue, trace) in &body.issues {
|
||||||
for path in trace.keys() {
|
for path in trace.keys() {
|
||||||
thread_header
|
write!(
|
||||||
.push_str(&format!("\n\n- issue {issue} found at file {path}"));
|
&mut thread_header,
|
||||||
|
"\n\n- issue {issue} found at file {path}"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if trace.is_empty() {
|
if trace.is_empty() {
|
||||||
thread_header.push_str(&format!("\n\n- issue {issue} found",));
|
write!(&mut thread_header, "\n\n- issue {issue} found").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -531,11 +531,9 @@ pub async fn edit_subscription(
|
|||||||
|
|
||||||
if let Some(payment_method) = &edit_subscription.payment_method
|
if let Some(payment_method) = &edit_subscription.payment_method
|
||||||
{
|
{
|
||||||
let payment_method_id = if let Ok(id) =
|
let Ok(payment_method_id) =
|
||||||
PaymentMethodId::from_str(payment_method)
|
PaymentMethodId::from_str(payment_method)
|
||||||
{
|
else {
|
||||||
id
|
|
||||||
} else {
|
|
||||||
return Err(ApiError::InvalidInput(
|
return Err(ApiError::InvalidInput(
|
||||||
"Invalid payment method id".to_string(),
|
"Invalid payment method id".to_string(),
|
||||||
));
|
));
|
||||||
@ -743,9 +741,7 @@ pub async fn edit_payment_method(
|
|||||||
|
|
||||||
let (id,) = info.into_inner();
|
let (id,) = info.into_inner();
|
||||||
|
|
||||||
let payment_method_id = if let Ok(id) = PaymentMethodId::from_str(&id) {
|
let Ok(payment_method_id) = PaymentMethodId::from_str(&id) else {
|
||||||
id
|
|
||||||
} else {
|
|
||||||
return Err(ApiError::NotFound);
|
return Err(ApiError::NotFound);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -766,10 +762,7 @@ pub async fn edit_payment_method(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if payment_method
|
if payment_method.customer.is_some_and(|x| x.id() == customer)
|
||||||
.customer
|
|
||||||
.map(|x| x.id() == customer)
|
|
||||||
.unwrap_or(false)
|
|
||||||
|| user.role.is_admin()
|
|| user.role.is_admin()
|
||||||
{
|
{
|
||||||
stripe::Customer::update(
|
stripe::Customer::update(
|
||||||
@ -812,9 +805,7 @@ pub async fn remove_payment_method(
|
|||||||
|
|
||||||
let (id,) = info.into_inner();
|
let (id,) = info.into_inner();
|
||||||
|
|
||||||
let payment_method_id = if let Ok(id) = PaymentMethodId::from_str(&id) {
|
let Ok(payment_method_id) = PaymentMethodId::from_str(&id) else {
|
||||||
id
|
|
||||||
} else {
|
|
||||||
return Err(ApiError::NotFound);
|
return Err(ApiError::NotFound);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -864,10 +855,7 @@ pub async fn remove_payment_method(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if payment_method
|
if payment_method.customer.is_some_and(|x| x.id() == customer)
|
||||||
.customer
|
|
||||||
.map(|x| x.id() == customer)
|
|
||||||
.unwrap_or(false)
|
|
||||||
|| user.role.is_admin()
|
|| user.role.is_admin()
|
||||||
{
|
{
|
||||||
stripe::PaymentMethod::detach(&stripe_client, &payment_method_id)
|
stripe::PaymentMethod::detach(&stripe_client, &payment_method_id)
|
||||||
@ -1437,8 +1425,6 @@ pub async fn stripe_webhook(
|
|||||||
pub user_subscription_item:
|
pub user_subscription_item:
|
||||||
Option<user_subscription_item::DBUserSubscription>,
|
Option<user_subscription_item::DBUserSubscription>,
|
||||||
pub payment_metadata: Option<PaymentRequestMetadata>,
|
pub payment_metadata: Option<PaymentRequestMetadata>,
|
||||||
#[allow(dead_code)]
|
|
||||||
pub charge_type: ChargeType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@ -1453,24 +1439,20 @@ pub async fn stripe_webhook(
|
|||||||
transaction: &mut Transaction<'_, Postgres>,
|
transaction: &mut Transaction<'_, Postgres>,
|
||||||
) -> Result<PaymentIntentMetadata, ApiError> {
|
) -> Result<PaymentIntentMetadata, ApiError> {
|
||||||
'metadata: {
|
'metadata: {
|
||||||
let user_id = if let Some(user_id) = metadata
|
let Some(user_id) = metadata
|
||||||
.get("modrinth_user_id")
|
.get("modrinth_user_id")
|
||||||
.and_then(|x| parse_base62(x).ok())
|
.and_then(|x| parse_base62(x).ok())
|
||||||
.map(|x| crate::database::models::ids::DBUserId(x as i64))
|
.map(|x| crate::database::models::ids::DBUserId(x as i64))
|
||||||
{
|
else {
|
||||||
user_id
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = if let Some(user) =
|
let Some(user) =
|
||||||
crate::database::models::user_item::DBUser::get_id(
|
crate::database::models::user_item::DBUser::get_id(
|
||||||
user_id, pool, redis,
|
user_id, pool, redis,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
else {
|
||||||
user
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1478,22 +1460,20 @@ pub async fn stripe_webhook(
|
|||||||
.get("modrinth_payment_metadata")
|
.get("modrinth_payment_metadata")
|
||||||
.and_then(|x| serde_json::from_str(x).ok());
|
.and_then(|x| serde_json::from_str(x).ok());
|
||||||
|
|
||||||
let charge_id = if let Some(charge_id) = metadata
|
let Some(charge_id) = metadata
|
||||||
.get("modrinth_charge_id")
|
.get("modrinth_charge_id")
|
||||||
.and_then(|x| parse_base62(x).ok())
|
.and_then(|x| parse_base62(x).ok())
|
||||||
.map(|x| crate::database::models::ids::DBChargeId(x as i64))
|
.map(|x| {
|
||||||
{
|
crate::database::models::ids::DBChargeId(x as i64)
|
||||||
charge_id
|
})
|
||||||
} else {
|
else {
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
let charge_type = if let Some(charge_type) = metadata
|
let Some(charge_type) = metadata
|
||||||
.get("modrinth_charge_type")
|
.get("modrinth_charge_type")
|
||||||
.map(|x| ChargeType::from_string(x))
|
.map(|x| ChargeType::from_string(x))
|
||||||
{
|
else {
|
||||||
charge_type
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1505,21 +1485,19 @@ pub async fn stripe_webhook(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
let price = if let Some(price) =
|
let Some(price) = product_item::DBProductPrice::get(
|
||||||
product_item::DBProductPrice::get(charge.price_id, pool)
|
charge.price_id,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
else {
|
||||||
price
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
let product = if let Some(product) =
|
let Some(product) =
|
||||||
product_item::DBProduct::get(price.product_id, pool)
|
product_item::DBProduct::get(price.product_id, pool)
|
||||||
.await?
|
.await?
|
||||||
{
|
else {
|
||||||
product
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1530,15 +1508,13 @@ pub async fn stripe_webhook(
|
|||||||
charge.upsert(transaction).await?;
|
charge.upsert(transaction).await?;
|
||||||
|
|
||||||
if let Some(subscription_id) = charge.subscription_id {
|
if let Some(subscription_id) = charge.subscription_id {
|
||||||
let mut subscription = if let Some(subscription) =
|
let Some(mut subscription) =
|
||||||
user_subscription_item::DBUserSubscription::get(
|
user_subscription_item::DBUserSubscription::get(
|
||||||
subscription_id,
|
subscription_id,
|
||||||
pool,
|
pool,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
else {
|
||||||
subscription
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1567,58 +1543,49 @@ pub async fn stripe_webhook(
|
|||||||
(charge, price, product, None)
|
(charge, price, product, None)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let price_id = if let Some(price_id) = metadata
|
let Some(price_id) = metadata
|
||||||
.get("modrinth_price_id")
|
.get("modrinth_price_id")
|
||||||
.and_then(|x| parse_base62(x).ok())
|
.and_then(|x| parse_base62(x).ok())
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
crate::database::models::ids::DBProductPriceId(
|
crate::database::models::ids::DBProductPriceId(
|
||||||
x as i64,
|
x as i64,
|
||||||
)
|
)
|
||||||
}) {
|
})
|
||||||
price_id
|
else {
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
let price = if let Some(price) =
|
let Some(price) =
|
||||||
product_item::DBProductPrice::get(price_id, pool)
|
product_item::DBProductPrice::get(price_id, pool)
|
||||||
.await?
|
.await?
|
||||||
{
|
else {
|
||||||
price
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
let product = if let Some(product) =
|
let Some(product) =
|
||||||
product_item::DBProduct::get(price.product_id, pool)
|
product_item::DBProduct::get(price.product_id, pool)
|
||||||
.await?
|
.await?
|
||||||
{
|
else {
|
||||||
product
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
let subscription = match &price.prices {
|
let subscription = match &price.prices {
|
||||||
Price::OneTime { .. } => None,
|
Price::OneTime { .. } => None,
|
||||||
Price::Recurring { intervals } => {
|
Price::Recurring { intervals } => {
|
||||||
let interval = if let Some(interval) = metadata
|
let Some(interval) = metadata
|
||||||
.get("modrinth_subscription_interval")
|
.get("modrinth_subscription_interval")
|
||||||
.map(|x| PriceDuration::from_string(x))
|
.map(|x| PriceDuration::from_string(x))
|
||||||
{
|
else {
|
||||||
interval
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
if intervals.get(&interval).is_some() {
|
if intervals.get(&interval).is_some() {
|
||||||
let subscription_id = if let Some(subscription_id) = metadata
|
let Some(subscription_id) = metadata
|
||||||
.get("modrinth_subscription_id")
|
.get("modrinth_subscription_id")
|
||||||
.and_then(|x| parse_base62(x).ok())
|
.and_then(|x| parse_base62(x).ok())
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
crate::database::models::ids::DBUserSubscriptionId(x as i64)
|
crate::database::models::ids::DBUserSubscriptionId(x as i64)
|
||||||
}) {
|
}) else {
|
||||||
subscription_id
|
|
||||||
} else {
|
|
||||||
break 'metadata;
|
break 'metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1687,7 +1654,6 @@ pub async fn stripe_webhook(
|
|||||||
charge_item: charge,
|
charge_item: charge,
|
||||||
user_subscription_item: subscription,
|
user_subscription_item: subscription,
|
||||||
payment_metadata,
|
payment_metadata,
|
||||||
charge_type,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2049,10 +2015,9 @@ pub async fn stripe_webhook(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !customer
|
if customer
|
||||||
.invoice_settings
|
.invoice_settings
|
||||||
.map(|x| x.default_payment_method.is_some())
|
.is_none_or(|x| x.default_payment_method.is_none())
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
stripe::Customer::update(
|
stripe::Customer::update(
|
||||||
&stripe_client,
|
&stripe_client,
|
||||||
@ -2187,12 +2152,10 @@ pub async fn index_subscriptions(pool: PgPool, redis: RedisPool) {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for charge in all_charges {
|
for charge in all_charges {
|
||||||
let subscription = if let Some(subscription) = all_subscriptions
|
let Some(subscription) = all_subscriptions
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|x| Some(x.id) == charge.subscription_id)
|
.find(|x| Some(x.id) == charge.subscription_id)
|
||||||
{
|
else {
|
||||||
subscription
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2200,29 +2163,23 @@ pub async fn index_subscriptions(pool: PgPool, redis: RedisPool) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let product_price = if let Some(product_price) = subscription_prices
|
let Some(product_price) = subscription_prices
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| x.id == subscription.price_id)
|
.find(|x| x.id == subscription.price_id)
|
||||||
{
|
else {
|
||||||
product_price
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let product = if let Some(product) = subscription_products
|
let Some(product) = subscription_products
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| x.id == product_price.product_id)
|
.find(|x| x.id == product_price.product_id)
|
||||||
{
|
else {
|
||||||
product
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = if let Some(user) =
|
let Some(user) =
|
||||||
users.iter().find(|x| x.id == subscription.user_id)
|
users.iter().find(|x| x.id == subscription.user_id)
|
||||||
{
|
else {
|
||||||
user
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2350,19 +2307,14 @@ pub async fn index_billing(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for mut charge in charges_to_do {
|
for mut charge in charges_to_do {
|
||||||
let product_price = if let Some(price) =
|
let Some(product_price) =
|
||||||
prices.iter().find(|x| x.id == charge.price_id)
|
prices.iter().find(|x| x.id == charge.price_id)
|
||||||
{
|
else {
|
||||||
price
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let user = if let Some(user) =
|
let Some(user) = users.iter().find(|x| x.id == charge.user_id)
|
||||||
users.iter().find(|x| x.id == charge.user_id)
|
else {
|
||||||
{
|
|
||||||
user
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2399,17 +2351,14 @@ pub async fn index_billing(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let currency = match Currency::from_str(
|
let Ok(currency) = Currency::from_str(
|
||||||
&product_price.currency_code.to_lowercase(),
|
&product_price.currency_code.to_lowercase(),
|
||||||
) {
|
) else {
|
||||||
Ok(x) => x,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(
|
warn!(
|
||||||
"Could not find currency for {}",
|
"Could not find currency for {}",
|
||||||
product_price.currency_code
|
product_price.currency_code
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut intent =
|
let mut intent =
|
||||||
|
|||||||
@ -1249,7 +1249,7 @@ pub async fn delete_auth_provider(
|
|||||||
.await?
|
.await?
|
||||||
.1;
|
.1;
|
||||||
|
|
||||||
if !user.auth_providers.map(|x| x.len() > 1).unwrap_or(false)
|
if user.auth_providers.is_none_or(|x| x.len() <= 1)
|
||||||
&& !user.has_password.unwrap_or(false)
|
&& !user.has_password.unwrap_or(false)
|
||||||
{
|
{
|
||||||
return Err(ApiError::InvalidInput(
|
return Err(ApiError::InvalidInput(
|
||||||
|
|||||||
@ -217,8 +217,7 @@ pub async fn ws_init(
|
|||||||
if status
|
if status
|
||||||
.profile_name
|
.profile_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|x| x.len() > 64)
|
.is_some_and(|x| x.len() > 64)
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -250,7 +250,7 @@ fn find_file<'a>(
|
|||||||
// Minecraft mods are not going to be both a mod and a modpack, so this minecraft-specific handling is fine
|
// Minecraft mods are not going to be both a mod and a modpack, so this minecraft-specific handling is fine
|
||||||
// As there can be multiple project types, returns the first allowable match
|
// As there can be multiple project types, returns the first allowable match
|
||||||
let mut fileexts = vec![];
|
let mut fileexts = vec![];
|
||||||
for project_type in version.project_types.iter() {
|
for project_type in &version.project_types {
|
||||||
match project_type.as_str() {
|
match project_type.as_str() {
|
||||||
"mod" => fileexts.push("jar"),
|
"mod" => fileexts.push("jar"),
|
||||||
"modpack" => fileexts.push("mrpack"),
|
"modpack" => fileexts.push("mrpack"),
|
||||||
@ -381,8 +381,10 @@ pub async fn version_file_sha1(
|
|||||||
|
|
||||||
Ok(find_file(&project_id, &vnum, &version, &file)
|
Ok(find_file(&project_id, &vnum, &version, &file)
|
||||||
.and_then(|file| file.hashes.get("sha1"))
|
.and_then(|file| file.hashes.get("sha1"))
|
||||||
.map(|hash_str| HttpResponse::Ok().body(hash_str.clone()))
|
.map_or_else(
|
||||||
.unwrap_or_else(|| HttpResponse::NotFound().body("")))
|
|| HttpResponse::NotFound().body(""),
|
||||||
|
|hash_str| HttpResponse::Ok().body(hash_str.clone()),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("maven/modrinth/{id}/{versionnum}/{file}.sha512")]
|
#[get("maven/modrinth/{id}/{versionnum}/{file}.sha512")]
|
||||||
@ -426,6 +428,8 @@ pub async fn version_file_sha512(
|
|||||||
|
|
||||||
Ok(find_file(&project_id, &vnum, &version, &file)
|
Ok(find_file(&project_id, &vnum, &version, &file)
|
||||||
.and_then(|file| file.hashes.get("sha512"))
|
.and_then(|file| file.hashes.get("sha512"))
|
||||||
.map(|hash_str| HttpResponse::Ok().body(hash_str.clone()))
|
.map_or_else(
|
||||||
.unwrap_or_else(|| HttpResponse::NotFound().body("")))
|
|| HttpResponse::NotFound().body(""),
|
||||||
|
|hash_str| HttpResponse::Ok().body(hash_str.clone()),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,7 +83,7 @@ pub async fn project_search(
|
|||||||
val
|
val
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
facet.to_string()
|
facet
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|||||||
@ -283,27 +283,22 @@ async fn get_example_version_fields(
|
|||||||
pool: Data<PgPool>,
|
pool: Data<PgPool>,
|
||||||
redis: &RedisPool,
|
redis: &RedisPool,
|
||||||
) -> Result<Option<Vec<VersionField>>, CreateError> {
|
) -> Result<Option<Vec<VersionField>>, CreateError> {
|
||||||
let project_id = match project_id {
|
let Some(project_id) = project_id else {
|
||||||
Some(project_id) => project_id,
|
return Ok(None);
|
||||||
None => return Ok(None),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let vid = match project_item::DBProject::get_id(
|
let Some(vid) =
|
||||||
project_id.into(),
|
project_item::DBProject::get_id(project_id.into(), &**pool, redis)
|
||||||
&**pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await?
|
.await?
|
||||||
.and_then(|p| p.versions.first().cloned())
|
.and_then(|p| p.versions.first().copied())
|
||||||
{
|
else {
|
||||||
Some(vid) => vid,
|
return Ok(None);
|
||||||
None => return Ok(None),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let example_version =
|
let Some(example_version) =
|
||||||
match version_item::DBVersion::get(vid, &**pool, redis).await? {
|
version_item::DBVersion::get(vid, &**pool, redis).await?
|
||||||
Some(version) => version,
|
else {
|
||||||
None => return Ok(None),
|
return Ok(None);
|
||||||
};
|
};
|
||||||
Ok(Some(example_version.version_fields))
|
Ok(Some(example_version.version_fields))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -256,13 +256,11 @@ pub async fn organization_get(
|
|||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
logged_in
|
logged_in
|
||||||
|| x.accepted
|
|| x.accepted
|
||||||
|| user_id
|
|| user_id.is_some_and(
|
||||||
.map(|y: crate::database::models::DBUserId| {
|
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||||
y == x.user_id
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.filter_map(|data| {
|
||||||
})
|
|
||||||
.flat_map(|data| {
|
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -345,13 +343,11 @@ pub async fn organizations_get(
|
|||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
logged_in
|
logged_in
|
||||||
|| x.accepted
|
|| x.accepted
|
||||||
|| user_id
|
|| user_id.is_some_and(
|
||||||
.map(|y: crate::database::models::DBUserId| {
|
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||||
y == x.user_id
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.filter_map(|data| {
|
||||||
})
|
|
||||||
.flat_map(|data| {
|
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -635,7 +631,7 @@ pub async fn organization_delete(
|
|||||||
.try_collect::<Vec<_>>()
|
.try_collect::<Vec<_>>()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for organization_project_team in organization_project_teams.iter() {
|
for organization_project_team in &organization_project_teams {
|
||||||
let new_id = crate::database::models::ids::generate_team_member_id(
|
let new_id = crate::database::models::ids::generate_team_member_id(
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -830,14 +830,13 @@ async fn get_user_balance(
|
|||||||
.fetch_optional(pool)
|
.fetch_optional(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (withdrawn, fees) = withdrawn
|
let (withdrawn, fees) =
|
||||||
.map(|x| {
|
withdrawn.map_or((Decimal::ZERO, Decimal::ZERO), |x| {
|
||||||
(
|
(
|
||||||
x.amount.unwrap_or(Decimal::ZERO),
|
x.amount.unwrap_or(Decimal::ZERO),
|
||||||
x.fee.unwrap_or(Decimal::ZERO),
|
x.fee.unwrap_or(Decimal::ZERO),
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.unwrap_or((Decimal::ZERO, Decimal::ZERO));
|
|
||||||
|
|
||||||
Ok(UserBalance {
|
Ok(UserBalance {
|
||||||
available: available.round_dp(16)
|
available: available.round_dp(16)
|
||||||
|
|||||||
@ -360,15 +360,14 @@ async fn project_create_inner(
|
|||||||
// The first multipart field must be named "data" and contain a
|
// The first multipart field must be named "data" and contain a
|
||||||
// JSON `ProjectCreateData` object.
|
// JSON `ProjectCreateData` object.
|
||||||
|
|
||||||
let mut field = payload
|
let mut field = payload.next().await.map_or_else(
|
||||||
.next()
|
|| {
|
||||||
.await
|
|
||||||
.map(|m| m.map_err(CreateError::MultipartError))
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
Err(CreateError::MissingValueError(String::from(
|
Err(CreateError::MissingValueError(String::from(
|
||||||
"No `data` field in multipart upload",
|
"No `data` field in multipart upload",
|
||||||
)))
|
)))
|
||||||
})?;
|
},
|
||||||
|
|m| m.map_err(CreateError::MultipartError),
|
||||||
|
)?;
|
||||||
|
|
||||||
let name = field.name().ok_or_else(|| {
|
let name = field.name().ok_or_else(|| {
|
||||||
CreateError::MissingValueError(String::from("Missing content name"))
|
CreateError::MissingValueError(String::from("Missing content name"))
|
||||||
@ -550,8 +549,8 @@ async fn project_create_inner(
|
|||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
// `index` is always valid for these lists
|
// `index` is always valid for these lists
|
||||||
let created_version = versions.get_mut(index).unwrap();
|
let created_version = &mut versions[index];
|
||||||
let version_data = project_create_data.initial_versions.get(index).unwrap();
|
let version_data = &project_create_data.initial_versions[index];
|
||||||
// TODO: maybe redundant is this calculation done elsewhere?
|
// TODO: maybe redundant is this calculation done elsewhere?
|
||||||
|
|
||||||
let existing_file_names = created_version
|
let existing_file_names = created_version
|
||||||
@ -670,10 +669,9 @@ async fn project_create_inner(
|
|||||||
&team_member,
|
&team_member,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !perms
|
if !perms.is_some_and(|x| {
|
||||||
.map(|x| x.contains(OrganizationPermissions::ADD_PROJECT))
|
x.contains(OrganizationPermissions::ADD_PROJECT)
|
||||||
.unwrap_or(false)
|
}) {
|
||||||
{
|
|
||||||
return Err(CreateError::CustomAuthenticationError(
|
return Err(CreateError::CustomAuthenticationError(
|
||||||
"You do not have the permissions to create projects in this organization!"
|
"You do not have the permissions to create projects in this organization!"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
|||||||
@ -448,7 +448,7 @@ pub async fn project_edit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if team_member.map(|x| !x.accepted).unwrap_or(true) {
|
if team_member.is_none_or(|x| !x.accepted) {
|
||||||
let notified_members = sqlx::query!(
|
let notified_members = sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT tm.user_id id
|
SELECT tm.user_id id
|
||||||
@ -2397,13 +2397,11 @@ pub async fn project_get_organization(
|
|||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
logged_in
|
logged_in
|
||||||
|| x.accepted
|
|| x.accepted
|
||||||
|| user_id
|
|| user_id.is_some_and(
|
||||||
.map(|y: crate::database::models::DBUserId| {
|
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||||
y == x.user_id
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.filter_map(|data| {
|
||||||
})
|
|
||||||
.flat_map(|data| {
|
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
|
|||||||
@ -148,16 +148,15 @@ pub async fn loader_fields_list(
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let loader_field_enum_id = match loader_field.field_type {
|
let (LoaderFieldType::Enum(loader_field_enum_id)
|
||||||
LoaderFieldType::Enum(enum_id)
|
| LoaderFieldType::ArrayEnum(loader_field_enum_id)) =
|
||||||
| LoaderFieldType::ArrayEnum(enum_id) => enum_id,
|
loader_field.field_type
|
||||||
_ => {
|
else {
|
||||||
return Err(ApiError::InvalidInput(format!(
|
return Err(ApiError::InvalidInput(format!(
|
||||||
"'{}' is not an enumerable field, but an '{}' field.",
|
"'{}' is not an enumerable field, but an '{}' field.",
|
||||||
query.loader_field,
|
query.loader_field,
|
||||||
loader_field.field_type.to_str()
|
loader_field.field_type.to_str()
|
||||||
)));
|
)));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let results: Vec<_> = if let Some(filters) = query.filters {
|
let results: Vec<_> = if let Some(filters) = query.filters {
|
||||||
|
|||||||
@ -101,13 +101,11 @@ pub async fn team_members_get_project(
|
|||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
logged_in
|
logged_in
|
||||||
|| x.accepted
|
|| x.accepted
|
||||||
|| user_id
|
|| user_id.is_some_and(
|
||||||
.map(|y: crate::database::models::DBUserId| {
|
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||||
y == x.user_id
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.filter_map(|data| {
|
||||||
})
|
|
||||||
.flat_map(|data| {
|
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -176,13 +174,11 @@ pub async fn team_members_get_organization(
|
|||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
logged_in
|
logged_in
|
||||||
|| x.accepted
|
|| x.accepted
|
||||||
|| user_id
|
|| user_id.is_some_and(
|
||||||
.map(|y: crate::database::models::DBUserId| {
|
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||||
y == x.user_id
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.filter_map(|data| {
|
||||||
})
|
|
||||||
.flat_map(|data| {
|
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -242,11 +238,11 @@ pub async fn team_members_get(
|
|||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
logged_in
|
logged_in
|
||||||
|| x.accepted
|
|| x.accepted
|
||||||
|| user_id
|
|| user_id.is_some_and(
|
||||||
.map(|y: crate::database::models::DBUserId| y == x.user_id)
|
|y: crate::database::models::DBUserId| y == x.user_id,
|
||||||
.unwrap_or(false)
|
)
|
||||||
})
|
})
|
||||||
.flat_map(|data| {
|
.filter_map(|data| {
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -319,7 +315,7 @@ pub async fn teams_get(
|
|||||||
let team_members = members
|
let team_members = members
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| logged_in || x.accepted)
|
.filter(|x| logged_in || x.accepted)
|
||||||
.flat_map(|data| {
|
.filter_map(|data| {
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -592,8 +588,7 @@ pub async fn add_team_member(
|
|||||||
};
|
};
|
||||||
if new_user_organization_team_member
|
if new_user_organization_team_member
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|tm| tm.is_owner)
|
.is_some_and(|tm| tm.is_owner)
|
||||||
.unwrap_or(false)
|
|
||||||
&& new_member.permissions != ProjectPermissions::all()
|
&& new_member.permissions != ProjectPermissions::all()
|
||||||
{
|
{
|
||||||
return Err(ApiError::InvalidInput(
|
return Err(ApiError::InvalidInput(
|
||||||
@ -748,12 +743,10 @@ pub async fn edit_team_member(
|
|||||||
|
|
||||||
if organization_team_member
|
if organization_team_member
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|x| x.is_owner)
|
.is_some_and(|x| x.is_owner)
|
||||||
.unwrap_or(false)
|
|
||||||
&& edit_member
|
&& edit_member
|
||||||
.permissions
|
.permissions
|
||||||
.map(|x| x != ProjectPermissions::all())
|
.is_some_and(|x| x != ProjectPermissions::all())
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
return Err(ApiError::CustomAuthentication(
|
return Err(ApiError::CustomAuthentication(
|
||||||
"You cannot override the project permissions of the organization owner!"
|
"You cannot override the project permissions of the organization owner!"
|
||||||
@ -1011,7 +1004,7 @@ pub async fn transfer_ownership(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// If the owner of the organization is a member of the project, remove them
|
// If the owner of the organization is a member of the project, remove them
|
||||||
for team_id in team_ids.iter() {
|
for team_id in &team_ids {
|
||||||
DBTeamMember::delete(
|
DBTeamMember::delete(
|
||||||
*team_id,
|
*team_id,
|
||||||
new_owner.user_id.into(),
|
new_owner.user_id.into(),
|
||||||
|
|||||||
@ -119,7 +119,7 @@ pub async fn filter_authorized_threads(
|
|||||||
let project_thread_ids = check_threads
|
let project_thread_ids = check_threads
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.type_ == ThreadType::Project)
|
.filter(|x| x.type_ == ThreadType::Project)
|
||||||
.flat_map(|x| x.project_id.map(|x| x.0))
|
.filter_map(|x| x.project_id.map(|x| x.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if !project_thread_ids.is_empty() {
|
if !project_thread_ids.is_empty() {
|
||||||
@ -148,13 +148,12 @@ pub async fn filter_authorized_threads(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let org_project_thread_ids = check_threads
|
let mut org_project_thread_ids = check_threads
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.type_ == ThreadType::Project)
|
.filter(|x| x.type_ == ThreadType::Project)
|
||||||
.flat_map(|x| x.project_id.map(|x| x.0))
|
.filter_map(|x| x.project_id.map(|x| x.0));
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if !org_project_thread_ids.is_empty() {
|
if org_project_thread_ids.next().is_some() {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT m.id FROM mods m
|
SELECT m.id FROM mods m
|
||||||
@ -184,7 +183,7 @@ pub async fn filter_authorized_threads(
|
|||||||
let report_thread_ids = check_threads
|
let report_thread_ids = check_threads
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| x.type_ == ThreadType::Report)
|
.filter(|x| x.type_ == ThreadType::Report)
|
||||||
.flat_map(|x| x.report_id.map(|x| x.0))
|
.filter_map(|x| x.report_id.map(|x| x.0))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if !report_thread_ids.is_empty() {
|
if !report_thread_ids.is_empty() {
|
||||||
|
|||||||
@ -207,7 +207,7 @@ pub async fn user_get(
|
|||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let response: crate::models::users::User =
|
let response: crate::models::users::User =
|
||||||
if auth_user.map(|x| x.role.is_admin()).unwrap_or(false) {
|
if auth_user.is_some_and(|x| x.role.is_admin()) {
|
||||||
crate::models::users::User::from_full(data)
|
crate::models::users::User::from_full(data)
|
||||||
} else {
|
} else {
|
||||||
data.into()
|
data.into()
|
||||||
@ -242,9 +242,8 @@ pub async fn collections_list(
|
|||||||
if let Some(id) = id_option.map(|x| x.id) {
|
if let Some(id) = id_option.map(|x| x.id) {
|
||||||
let user_id: UserId = id.into();
|
let user_id: UserId = id.into();
|
||||||
|
|
||||||
let can_view_private = user
|
let can_view_private =
|
||||||
.map(|y| y.role.is_mod() || y.id == user_id)
|
user.is_some_and(|y| y.role.is_mod() || y.id == user_id);
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
let project_data = DBUser::get_collections(id, &**pool).await?;
|
let project_data = DBUser::get_collections(id, &**pool).await?;
|
||||||
|
|
||||||
@ -334,7 +333,7 @@ pub async fn orgs_list(
|
|||||||
let team_members: Vec<_> = members_data
|
let team_members: Vec<_> = members_data
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|x| logged_in || x.accepted || id == x.user_id)
|
.filter(|x| logged_in || x.accepted || id == x.user_id)
|
||||||
.flat_map(|data| {
|
.filter_map(|data| {
|
||||||
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
crate::models::teams::TeamMember::from(
|
crate::models::teams::TeamMember::from(
|
||||||
data,
|
data,
|
||||||
@ -412,8 +411,7 @@ pub async fn user_edit(
|
|||||||
|
|
||||||
if existing_user_id_option
|
if existing_user_id_option
|
||||||
.map(|x| UserId::from(x.id))
|
.map(|x| UserId::from(x.id))
|
||||||
.map(|id| id == user.id)
|
.is_none_or(|id| id == user.id)
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
{
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
|
|||||||
@ -606,13 +606,10 @@ async fn upload_file_to_version_inner(
|
|||||||
|
|
||||||
let result = models::DBVersion::get(version_id, &**client, &redis).await?;
|
let result = models::DBVersion::get(version_id, &**client, &redis).await?;
|
||||||
|
|
||||||
let version = match result {
|
let Some(version) = result else {
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
return Err(CreateError::InvalidInput(
|
return Err(CreateError::InvalidInput(
|
||||||
"An invalid version id was supplied".to_string(),
|
"An invalid version id was supplied".to_string(),
|
||||||
));
|
));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let all_loaders =
|
let all_loaders =
|
||||||
@ -1065,7 +1062,7 @@ pub fn try_create_version_fields(
|
|||||||
.filter(|lf| !lf.optional)
|
.filter(|lf| !lf.optional)
|
||||||
.map(|lf| lf.field.clone())
|
.map(|lf| lf.field.clone())
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
for (key, value) in submitted_fields.iter() {
|
for (key, value) in submitted_fields {
|
||||||
let loader_field = loader_fields
|
let loader_field = loader_fields
|
||||||
.iter()
|
.iter()
|
||||||
.find(|lf| &lf.field == key)
|
.find(|lf| &lf.field == key)
|
||||||
|
|||||||
@ -794,8 +794,7 @@ pub async fn version_list(
|
|||||||
.filter(|version| {
|
.filter(|version| {
|
||||||
filters
|
filters
|
||||||
.featured
|
.featured
|
||||||
.map(|featured| featured == version.inner.featured)
|
.is_none_or(|featured| featured == version.inner.featured)
|
||||||
.unwrap_or(true)
|
|
||||||
})
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -830,9 +829,7 @@ pub async fn version_list(
|
|||||||
}
|
}
|
||||||
|
|
||||||
joined_filters.into_iter().for_each(|filter| {
|
joined_filters.into_iter().for_each(|filter| {
|
||||||
versions
|
if let Some(version) = versions.iter().find(|version| {
|
||||||
.iter()
|
|
||||||
.find(|version| {
|
|
||||||
// TODO: This is the bandaid fix for detecting auto-featured versions.
|
// TODO: This is the bandaid fix for detecting auto-featured versions.
|
||||||
let game_versions = version
|
let game_versions = version
|
||||||
.version_fields
|
.version_fields
|
||||||
@ -843,9 +840,9 @@ pub async fn version_list(
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
game_versions.contains(&filter.0.version)
|
game_versions.contains(&filter.0.version)
|
||||||
&& version.loaders.contains(&filter.1.loader)
|
&& version.loaders.contains(&filter.1.loader)
|
||||||
})
|
}) {
|
||||||
.map(|version| response.push(version.clone()))
|
response.push(version.clone());
|
||||||
.unwrap_or(());
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if response.is_empty() {
|
if response.is_empty() {
|
||||||
|
|||||||
@ -522,7 +522,7 @@ async fn index_versions(
|
|||||||
// Convert to partial versions
|
// Convert to partial versions
|
||||||
let mut res_versions: HashMap<DBProjectId, Vec<PartialVersion>> =
|
let mut res_versions: HashMap<DBProjectId, Vec<PartialVersion>> =
|
||||||
HashMap::new();
|
HashMap::new();
|
||||||
for (project_id, version_ids) in versions.iter() {
|
for (project_id, version_ids) in &versions {
|
||||||
for version_id in version_ids {
|
for version_id in version_ids {
|
||||||
// Extract version-specific data fetched
|
// Extract version-specific data fetched
|
||||||
// We use 'remove' as every version is only in the map once
|
// We use 'remove' as every version is only in the map once
|
||||||
|
|||||||
@ -13,7 +13,6 @@ pub struct MultipartSegment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum MultipartSegmentData {
|
pub enum MultipartSegmentData {
|
||||||
Text(String),
|
Text(String),
|
||||||
Binary(Vec<u8>),
|
Binary(Vec<u8>),
|
||||||
|
|||||||
@ -98,13 +98,12 @@ pub async fn upload_image_optimized(
|
|||||||
|
|
||||||
let url = format!("{}/{}", cdn_url, upload_data.file_name);
|
let url = format!("{}/{}", cdn_url, upload_data.file_name);
|
||||||
Ok(UploadImageResult {
|
Ok(UploadImageResult {
|
||||||
url: processed_upload_data
|
url: processed_upload_data.clone().map_or_else(
|
||||||
.clone()
|
|| url.clone(),
|
||||||
.map(|x| format!("{}/{}", cdn_url, x.file_name))
|
|x| format!("{}/{}", cdn_url, x.file_name),
|
||||||
.unwrap_or_else(|| url.clone()),
|
),
|
||||||
url_path: processed_upload_data
|
url_path: processed_upload_data
|
||||||
.map(|x| x.file_name)
|
.map_or_else(|| upload_data.file_name.clone(), |x| x.file_name),
|
||||||
.unwrap_or_else(|| upload_data.file_name.clone()),
|
|
||||||
|
|
||||||
raw_url: url,
|
raw_url: url,
|
||||||
raw_url_path: upload_data.file_name,
|
raw_url_path: upload_data.file_name,
|
||||||
@ -119,7 +118,7 @@ fn process_image(
|
|||||||
min_aspect_ratio: Option<f32>,
|
min_aspect_ratio: Option<f32>,
|
||||||
) -> Result<(bytes::Bytes, String), ImageError> {
|
) -> Result<(bytes::Bytes, String), ImageError> {
|
||||||
if content_type.to_lowercase() == "image/gif" {
|
if content_type.to_lowercase() == "image/gif" {
|
||||||
return Ok((image_bytes.clone(), "gif".to_string()));
|
return Ok((image_bytes, "gif".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut img = image::load_from_memory(&image_bytes)?;
|
let mut img = image::load_from_memory(&image_bytes)?;
|
||||||
|
|||||||
@ -56,9 +56,7 @@ impl AsyncRateLimiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_rate_limit(&self, key: &str) -> RateLimitDecision {
|
pub async fn check_rate_limit(&self, key: &str) -> RateLimitDecision {
|
||||||
let mut conn = match self.redis_pool.connect().await {
|
let Ok(mut conn) = self.redis_pool.connect().await else {
|
||||||
Ok(conn) => conn,
|
|
||||||
Err(_) => {
|
|
||||||
// If Redis is unavailable, allow the request but with reduced limit
|
// If Redis is unavailable, allow the request but with reduced limit
|
||||||
return RateLimitDecision {
|
return RateLimitDecision {
|
||||||
allowed: true,
|
allowed: true,
|
||||||
@ -67,7 +65,6 @@ impl AsyncRateLimiter {
|
|||||||
reset_after_ms: 60_000, // 1 minute
|
reset_after_ms: 60_000, // 1 minute
|
||||||
retry_after_ms: None,
|
retry_after_ms: None,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get current time in nanoseconds since UNIX epoch
|
// Get current time in nanoseconds since UNIX epoch
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::sync::LazyLock;
|
use std::{fmt::Write, sync::LazyLock};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@ -44,15 +44,17 @@ pub fn validation_errors_to_string(
|
|||||||
ValidationErrorsKind::Field(errors) => {
|
ValidationErrorsKind::Field(errors) => {
|
||||||
if let Some(error) = errors.first() {
|
if let Some(error) = errors.first() {
|
||||||
if let Some(adder) = adder {
|
if let Some(adder) = adder {
|
||||||
output.push_str(&format!(
|
write!(
|
||||||
"Field {} {} failed validation with error: {}",
|
&mut output,
|
||||||
field, adder, error.code
|
"Field {field} {adder} failed validation with error: {}",
|
||||||
));
|
error.code
|
||||||
|
).unwrap();
|
||||||
} else {
|
} else {
|
||||||
output.push_str(&format!(
|
write!(
|
||||||
"Field {} failed validation with error: {}",
|
&mut output,
|
||||||
field, error.code
|
"Field {field} failed validation with error: {}",
|
||||||
));
|
error.code
|
||||||
|
).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,6 @@ pub enum SupportedGameVersions {
|
|||||||
All,
|
All,
|
||||||
PastDate(DateTime<Utc>),
|
PastDate(DateTime<Utc>),
|
||||||
Range(DateTime<Utc>, DateTime<Utc>),
|
Range(DateTime<Utc>, DateTime<Utc>),
|
||||||
#[allow(dead_code)]
|
|
||||||
Custom(Vec<MinecraftGameVersion>),
|
Custom(Vec<MinecraftGameVersion>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,8 +231,7 @@ fn game_version_supported(
|
|||||||
all_game_versions
|
all_game_versions
|
||||||
.iter()
|
.iter()
|
||||||
.find(|y| y.version == x.version)
|
.find(|y| y.version == x.version)
|
||||||
.map(|x| x.created > date)
|
.is_some_and(|x| x.created > date)
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
SupportedGameVersions::Range(before, after) => {
|
SupportedGameVersions::Range(before, after) => {
|
||||||
@ -241,8 +239,7 @@ fn game_version_supported(
|
|||||||
all_game_versions
|
all_game_versions
|
||||||
.iter()
|
.iter()
|
||||||
.find(|y| y.version == x.version)
|
.find(|y| y.version == x.version)
|
||||||
.map(|x| x.created > before && x.created < after)
|
.is_some_and(|x| x.created > before && x.created < after)
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
SupportedGameVersions::Custom(versions) => {
|
SupportedGameVersions::Custom(versions) => {
|
||||||
|
|||||||
@ -28,10 +28,7 @@ impl super::Validator for ModpackValidator {
|
|||||||
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
archive: &mut ZipArchive<Cursor<bytes::Bytes>>,
|
||||||
) -> Result<ValidationResult, ValidationError> {
|
) -> Result<ValidationResult, ValidationError> {
|
||||||
let pack: PackFormat = {
|
let pack: PackFormat = {
|
||||||
let mut file =
|
let Ok(mut file) = archive.by_name("modrinth.index.json") else {
|
||||||
if let Ok(file) = archive.by_name("modrinth.index.json") {
|
|
||||||
file
|
|
||||||
} else {
|
|
||||||
return Ok(ValidationResult::Warning(
|
return Ok(ValidationResult::Warning(
|
||||||
"Pack manifest is missing.",
|
"Pack manifest is missing.",
|
||||||
));
|
));
|
||||||
@ -109,7 +106,7 @@ impl super::Validator for ModpackValidator {
|
|||||||
|| x.starts_with("overrides/shaderpacks")
|
|| x.starts_with("overrides/shaderpacks")
|
||||||
|| x.starts_with("client-overrides/shaderpacks"))
|
|| x.starts_with("client-overrides/shaderpacks"))
|
||||||
})
|
})
|
||||||
.flat_map(|x| x.rsplit('/').next().map(|x| x.to_string()))
|
.filter_map(|x| x.rsplit('/').next().map(|x| x.to_string()))
|
||||||
.collect::<Vec<String>>(),
|
.collect::<Vec<String>>(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use labrinth::models::teams::ProjectPermissions;
|
|||||||
use labrinth::queue::payouts;
|
use labrinth::queue::payouts;
|
||||||
use rust_decimal::{Decimal, prelude::ToPrimitive};
|
use rust_decimal::{Decimal, prelude::ToPrimitive};
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
pub async fn analytics_revenue() {
|
pub async fn analytics_revenue() {
|
||||||
@ -50,7 +50,7 @@ pub async fn analytics_revenue() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let project_id = parse_base62(&alpha_project_id).unwrap() as i64;
|
let project_id = parse_base62(&alpha_project_id).unwrap() as i64;
|
||||||
for (money, time) in money_time_pairs.iter() {
|
for (money, time) in &money_time_pairs {
|
||||||
insert_user_ids.push(USER_USER_ID_PARSED);
|
insert_user_ids.push(USER_USER_ID_PARSED);
|
||||||
insert_project_ids.push(project_id);
|
insert_project_ids.push(project_id);
|
||||||
insert_payouts.push(Decimal::from_f64_retain(*money).unwrap());
|
insert_payouts.push(Decimal::from_f64_retain(*money).unwrap());
|
||||||
@ -87,7 +87,7 @@ pub async fn analytics_revenue() {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(analytics.len(), 1); // 1 project
|
assert_eq!(analytics.len(), 1); // 1 project
|
||||||
let project_analytics = analytics.get(&alpha_project_id).unwrap();
|
let project_analytics = &analytics[&alpha_project_id];
|
||||||
assert_eq!(project_analytics.len(), 8); // 1 days cut off, and 2 points take place on the same day. note that the day exactly 14 days ago is included
|
assert_eq!(project_analytics.len(), 8); // 1 days cut off, and 2 points take place on the same day. note that the day exactly 14 days ago is included
|
||||||
// sorted_by_key, values in the order of smallest to largest key
|
// sorted_by_key, values in the order of smallest to largest key
|
||||||
let (sorted_keys, sorted_by_key): (Vec<i64>, Vec<Decimal>) =
|
let (sorted_keys, sorted_by_key): (Vec<i64>, Vec<Decimal>) =
|
||||||
@ -117,7 +117,7 @@ pub async fn analytics_revenue() {
|
|||||||
USER_USER_PAT,
|
USER_USER_PAT,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let project_analytics = analytics.get(&alpha_project_id).unwrap();
|
let project_analytics = &analytics[&alpha_project_id];
|
||||||
assert_eq!(project_analytics.len(), 9); // and 2 points take place on the same day
|
assert_eq!(project_analytics.len(), 9); // and 2 points take place on the same day
|
||||||
let (sorted_keys, sorted_by_key): (Vec<i64>, Vec<Decimal>) =
|
let (sorted_keys, sorted_by_key): (Vec<i64>, Vec<Decimal>) =
|
||||||
project_analytics
|
project_analytics
|
||||||
|
|||||||
@ -31,13 +31,12 @@ use serde::Deserialize;
|
|||||||
// as the environment generator for both uses common fields.
|
// as the environment generator for both uses common fields.
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonProject {
|
pub struct CommonProject {
|
||||||
// For example, for CommonProject, we do not include:
|
// For example, for CommonProject, we do not include:
|
||||||
// - game_versions (v2 only)
|
// - game_versions (v2 only)
|
||||||
// - loader_fields (v3 only)
|
// - loader_fields (v3 only)
|
||||||
// - etc.
|
// - etc.
|
||||||
// For any tests that require those fields, we make a separate test with separate API functions tht do not use Common models.
|
// For any tests that require those fields, we make a separate test with separate API functions that do not use Common models.
|
||||||
pub id: ProjectId,
|
pub id: ProjectId,
|
||||||
pub slug: Option<String>,
|
pub slug: Option<String>,
|
||||||
pub organization: Option<OrganizationId>,
|
pub organization: Option<OrganizationId>,
|
||||||
@ -62,7 +61,6 @@ pub struct CommonProject {
|
|||||||
pub monetization_status: MonetizationStatus,
|
pub monetization_status: MonetizationStatus,
|
||||||
}
|
}
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize, Clone)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonVersion {
|
pub struct CommonVersion {
|
||||||
pub id: VersionId,
|
pub id: VersionId,
|
||||||
pub loaders: Vec<String>,
|
pub loaders: Vec<String>,
|
||||||
@ -82,7 +80,6 @@ pub struct CommonVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonLoaderData {
|
pub struct CommonLoaderData {
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -90,7 +87,6 @@ pub struct CommonLoaderData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonCategoryData {
|
pub struct CommonCategoryData {
|
||||||
pub icon: String,
|
pub icon: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -100,7 +96,6 @@ pub struct CommonCategoryData {
|
|||||||
|
|
||||||
/// A member of a team
|
/// A member of a team
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonTeamMember {
|
pub struct CommonTeamMember {
|
||||||
pub team_id: TeamId,
|
pub team_id: TeamId,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
@ -114,7 +109,6 @@ pub struct CommonTeamMember {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonNotification {
|
pub struct CommonNotification {
|
||||||
pub id: NotificationId,
|
pub id: NotificationId,
|
||||||
pub user_id: UserId,
|
pub user_id: UserId,
|
||||||
@ -127,7 +121,6 @@ pub struct CommonNotification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonNotificationAction {
|
pub struct CommonNotificationAction {
|
||||||
pub action_route: (String, String),
|
pub action_route: (String, String),
|
||||||
}
|
}
|
||||||
@ -153,7 +146,6 @@ impl CommonItemType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonReport {
|
pub struct CommonReport {
|
||||||
pub id: ReportId,
|
pub id: ReportId,
|
||||||
pub report_type: String,
|
pub report_type: String,
|
||||||
@ -175,7 +167,6 @@ pub enum LegacyItemType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonThread {
|
pub struct CommonThread {
|
||||||
pub id: ThreadId,
|
pub id: ThreadId,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
@ -187,7 +178,6 @@ pub struct CommonThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonThreadMessage {
|
pub struct CommonThreadMessage {
|
||||||
pub id: ThreadMessageId,
|
pub id: ThreadMessageId,
|
||||||
pub author_id: Option<UserId>,
|
pub author_id: Option<UserId>,
|
||||||
@ -196,7 +186,6 @@ pub struct CommonThreadMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum CommonMessageBody {
|
pub enum CommonMessageBody {
|
||||||
Text {
|
Text {
|
||||||
body: String,
|
body: String,
|
||||||
@ -216,7 +205,6 @@ pub enum CommonMessageBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum CommonThreadType {
|
pub enum CommonThreadType {
|
||||||
Report,
|
Report,
|
||||||
Project,
|
Project,
|
||||||
@ -224,7 +212,6 @@ pub enum CommonThreadType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct CommonUser {
|
pub struct CommonUser {
|
||||||
pub id: UserId,
|
pub id: UserId,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|||||||
@ -5,21 +5,18 @@ use labrinth::util::actix::MultipartSegment;
|
|||||||
|
|
||||||
use crate::common::dummy_data::TestFile;
|
use crate::common::dummy_data::TestFile;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct ProjectCreationRequestData {
|
pub struct ProjectCreationRequestData {
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
pub jar: Option<TestFile>,
|
pub jar: Option<TestFile>,
|
||||||
pub segment_data: Vec<MultipartSegment>,
|
pub segment_data: Vec<MultipartSegment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct VersionCreationRequestData {
|
pub struct VersionCreationRequestData {
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub jar: Option<TestFile>,
|
pub jar: Option<TestFile>,
|
||||||
pub segment_data: Vec<MultipartSegment>,
|
pub segment_data: Vec<MultipartSegment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct ImageData {
|
pub struct ImageData {
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
pub extension: String,
|
pub extension: String,
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
api_common::{Api, ApiBuildable},
|
api_common::{Api, ApiBuildable},
|
||||||
environment::LocalService,
|
environment::LocalService,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt::Write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_status,
|
assert_status,
|
||||||
@ -490,13 +490,13 @@ impl ApiProject for ApiV2 {
|
|||||||
featured = featured
|
featured = featured
|
||||||
);
|
);
|
||||||
if let Some(title) = title {
|
if let Some(title) = title {
|
||||||
url.push_str(&format!("&title={title}"));
|
write!(&mut url, "&title={title}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(description) = description {
|
if let Some(description) = description {
|
||||||
url.push_str(&format!("&description={description}"));
|
write!(&mut url, "&description={description}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(ordering) = ordering {
|
if let Some(ordering) = ordering {
|
||||||
url.push_str(&format!("&ordering={ordering}"));
|
write!(&mut url, "&ordering={ordering}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
@ -521,11 +521,12 @@ impl ApiProject for ApiV2 {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (key, value) in patch {
|
for (key, value) in patch {
|
||||||
url.push_str(&format!(
|
write!(
|
||||||
|
&mut url,
|
||||||
"&{key}={value}",
|
"&{key}={value}",
|
||||||
key = key,
|
|
||||||
value = urlencoding::encode(&value)
|
value = urlencoding::encode(&value)
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
@ -90,7 +89,7 @@ pub fn get_public_project_creation_data_json(
|
|||||||
{
|
{
|
||||||
"title": format!("Test Project {slug}"),
|
"title": format!("Test Project {slug}"),
|
||||||
"slug": slug,
|
"slug": slug,
|
||||||
"project_type": version_jar.as_ref().map(|f| f.project_type()).unwrap_or("mod".to_string()),
|
"project_type": version_jar.as_ref().map_or("mod".to_string(), |f| f.project_type()),
|
||||||
"description": "A dummy project for testing with.",
|
"description": "A dummy project for testing with.",
|
||||||
"body": "This project is approved, and versions are listed.",
|
"body": "This project is approved, and versions are listed.",
|
||||||
"client_side": "required",
|
"client_side": "required",
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ApiV2,
|
ApiV2,
|
||||||
@ -383,32 +384,36 @@ impl ApiVersion for ApiV2 {
|
|||||||
) -> ServiceResponse {
|
) -> ServiceResponse {
|
||||||
let mut query_string = String::new();
|
let mut query_string = String::new();
|
||||||
if let Some(game_versions) = game_versions {
|
if let Some(game_versions) = game_versions {
|
||||||
query_string.push_str(&format!(
|
write!(
|
||||||
|
&mut query_string,
|
||||||
"&game_versions={}",
|
"&game_versions={}",
|
||||||
urlencoding::encode(
|
urlencoding::encode(
|
||||||
&serde_json::to_string(&game_versions).unwrap()
|
&serde_json::to_string(&game_versions).unwrap()
|
||||||
)
|
)
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
if let Some(loaders) = loaders {
|
if let Some(loaders) = loaders {
|
||||||
query_string.push_str(&format!(
|
write!(
|
||||||
|
&mut query_string,
|
||||||
"&loaders={}",
|
"&loaders={}",
|
||||||
urlencoding::encode(&serde_json::to_string(&loaders).unwrap())
|
urlencoding::encode(&serde_json::to_string(&loaders).unwrap())
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
if let Some(featured) = featured {
|
if let Some(featured) = featured {
|
||||||
query_string.push_str(&format!("&featured={featured}"));
|
write!(&mut query_string, "&featured={featured}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(version_type) = version_type {
|
if let Some(version_type) = version_type {
|
||||||
query_string.push_str(&format!("&version_type={version_type}"));
|
write!(&mut query_string, "&version_type={version_type}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(limit) = limit {
|
if let Some(limit) = limit {
|
||||||
let limit = limit.to_string();
|
let limit = limit.to_string();
|
||||||
query_string.push_str(&format!("&limit={limit}"));
|
write!(&mut query_string, "&limit={limit}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(offset) = offset {
|
if let Some(offset) = offset {
|
||||||
let offset = offset.to_string();
|
let offset = offset.to_string();
|
||||||
query_string.push_str(&format!("&offset={offset}"));
|
write!(&mut query_string, "&offset={offset}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::get()
|
let req = test::TestRequest::get()
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
api_common::{Api, ApiBuildable},
|
api_common::{Api, ApiBuildable},
|
||||||
environment::LocalService,
|
environment::LocalService,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt::Write};
|
||||||
|
|
||||||
use actix_http::StatusCode;
|
use actix_http::StatusCode;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
@ -363,13 +363,13 @@ impl ApiProject for ApiV3 {
|
|||||||
featured = featured
|
featured = featured
|
||||||
);
|
);
|
||||||
if let Some(title) = title {
|
if let Some(title) = title {
|
||||||
url.push_str(&format!("&title={title}"));
|
write!(&mut url, "&title={title}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(description) = description {
|
if let Some(description) = description {
|
||||||
url.push_str(&format!("&description={description}"));
|
write!(&mut url, "&description={description}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(ordering) = ordering {
|
if let Some(ordering) = ordering {
|
||||||
url.push_str(&format!("&ordering={ordering}"));
|
write!(&mut url, "&ordering={ordering}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
@ -394,11 +394,12 @@ impl ApiProject for ApiV3 {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (key, value) in patch {
|
for (key, value) in patch {
|
||||||
url.push_str(&format!(
|
write!(
|
||||||
|
&mut url,
|
||||||
"&{key}={value}",
|
"&{key}={value}",
|
||||||
key = key,
|
|
||||||
value = urlencoding::encode(&value)
|
value = urlencoding::encode(&value)
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
@ -593,17 +594,17 @@ impl ApiV3 {
|
|||||||
let start_date = start_date.to_rfc3339();
|
let start_date = start_date.to_rfc3339();
|
||||||
// let start_date = serde_json::to_string(&start_date).unwrap();
|
// let start_date = serde_json::to_string(&start_date).unwrap();
|
||||||
let start_date = urlencoding::encode(&start_date);
|
let start_date = urlencoding::encode(&start_date);
|
||||||
extra_args.push_str(&format!("&start_date={start_date}"));
|
write!(&mut extra_args, "&start_date={start_date}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(end_date) = end_date {
|
if let Some(end_date) = end_date {
|
||||||
let end_date = end_date.to_rfc3339();
|
let end_date = end_date.to_rfc3339();
|
||||||
// let end_date = serde_json::to_string(&end_date).unwrap();
|
// let end_date = serde_json::to_string(&end_date).unwrap();
|
||||||
let end_date = urlencoding::encode(&end_date);
|
let end_date = urlencoding::encode(&end_date);
|
||||||
extra_args.push_str(&format!("&end_date={end_date}"));
|
write!(&mut extra_args, "&end_date={end_date}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(resolution_minutes) = resolution_minutes {
|
if let Some(resolution_minutes) = resolution_minutes {
|
||||||
extra_args
|
write!(&mut extra_args, "&resolution_minutes={resolution_minutes}")
|
||||||
.push_str(&format!("&resolution_minutes={resolution_minutes}"));
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::get()
|
let req = test::TestRequest::get()
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ApiV3,
|
ApiV3,
|
||||||
@ -416,32 +417,36 @@ impl ApiVersion for ApiV3 {
|
|||||||
) -> ServiceResponse {
|
) -> ServiceResponse {
|
||||||
let mut query_string = String::new();
|
let mut query_string = String::new();
|
||||||
if let Some(game_versions) = game_versions {
|
if let Some(game_versions) = game_versions {
|
||||||
query_string.push_str(&format!(
|
write!(
|
||||||
|
&mut query_string,
|
||||||
"&game_versions={}",
|
"&game_versions={}",
|
||||||
urlencoding::encode(
|
urlencoding::encode(
|
||||||
&serde_json::to_string(&game_versions).unwrap()
|
&serde_json::to_string(&game_versions).unwrap()
|
||||||
)
|
)
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
if let Some(loaders) = loaders {
|
if let Some(loaders) = loaders {
|
||||||
query_string.push_str(&format!(
|
write!(
|
||||||
|
&mut query_string,
|
||||||
"&loaders={}",
|
"&loaders={}",
|
||||||
urlencoding::encode(&serde_json::to_string(&loaders).unwrap())
|
urlencoding::encode(&serde_json::to_string(&loaders).unwrap())
|
||||||
));
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
if let Some(featured) = featured {
|
if let Some(featured) = featured {
|
||||||
query_string.push_str(&format!("&featured={featured}"));
|
write!(&mut query_string, "&featured={featured}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(version_type) = version_type {
|
if let Some(version_type) = version_type {
|
||||||
query_string.push_str(&format!("&version_type={version_type}"));
|
write!(&mut query_string, "&version_type={version_type}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(limit) = limit {
|
if let Some(limit) = limit {
|
||||||
let limit = limit.to_string();
|
let limit = limit.to_string();
|
||||||
query_string.push_str(&format!("&limit={limit}"));
|
write!(&mut query_string, "&limit={limit}").unwrap();
|
||||||
}
|
}
|
||||||
if let Some(offset) = offset {
|
if let Some(offset) = offset {
|
||||||
let offset = offset.to_string();
|
let offset = offset.to_string();
|
||||||
query_string.push_str(&format!("&offset={offset}"));
|
write!(&mut query_string, "&offset={offset}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::get()
|
let req = test::TestRequest::get()
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use crate::common::get_json_val_str;
|
use crate::common::get_json_val_str;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use labrinth::models::v3::projects::Version;
|
use labrinth::models::v3::projects::Version;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use labrinth::{database::redis::RedisPool, search};
|
use labrinth::{database::redis::RedisPool, search};
|
||||||
use sqlx::{PgPool, postgres::PgPoolOptions};
|
use sqlx::{PgPool, postgres::PgPoolOptions};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use std::io::{Cursor, Write};
|
use std::io::{Cursor, Write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -28,7 +27,6 @@ use super::{database::USER_USER_ID, get_json_val_str};
|
|||||||
|
|
||||||
pub const DUMMY_DATA_UPDATE: i64 = 7;
|
pub const DUMMY_DATA_UPDATE: i64 = 7;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub const DUMMY_CATEGORIES: &[&str] = &[
|
pub const DUMMY_CATEGORIES: &[&str] = &[
|
||||||
"combat",
|
"combat",
|
||||||
"decoration",
|
"decoration",
|
||||||
@ -41,7 +39,6 @@ pub const DUMMY_CATEGORIES: &[&str] = &[
|
|||||||
|
|
||||||
pub const DUMMY_OAUTH_CLIENT_ALPHA_SECRET: &str = "abcdefghijklmnopqrstuvwxyz";
|
pub const DUMMY_OAUTH_CLIENT_ALPHA_SECRET: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum TestFile {
|
pub enum TestFile {
|
||||||
DummyProjectAlpha,
|
DummyProjectAlpha,
|
||||||
@ -173,7 +170,6 @@ impl TestFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum DummyImage {
|
pub enum DummyImage {
|
||||||
SmallIcon, // 200x200
|
SmallIcon, // 200x200
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
api_common::{Api, ApiBuildable, generic::GenericApi},
|
api_common::{Api, ApiBuildable, generic::GenericApi},
|
||||||
api_v2::ApiV2,
|
api_v2::ApiV2,
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use labrinth::{
|
use labrinth::{
|
||||||
database::{self, models::generate_pat_id},
|
database::{self, models::generate_pat_id},
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use actix_http::StatusCode;
|
use actix_http::StatusCode;
|
||||||
use actix_web::{dev::ServiceResponse, test};
|
use actix_web::{dev::ServiceResponse, test};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use actix_web::{dev::ServiceResponse, test};
|
use actix_web::{dev::ServiceResponse, test};
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use labrinth::models::pats::Scopes;
|
use labrinth::models::pats::Scopes;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use actix_http::StatusCode;
|
use actix_http::StatusCode;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use common::api_v3::ApiV3;
|
|||||||
use common::database::USER_USER_PAT;
|
use common::database::USER_USER_PAT;
|
||||||
use common::environment::{TestEnvironment, with_test_environment};
|
use common::environment::{TestEnvironment, with_test_environment};
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
pub async fn error_404_body() {
|
pub async fn error_404_body() {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use common::{
|
|||||||
environment::{TestEnvironment, with_test_environment},
|
environment::{TestEnvironment, with_test_environment},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn get_games() {
|
async fn get_games() {
|
||||||
|
|||||||
@ -17,8 +17,7 @@ use crate::common::dummy_data::{
|
|||||||
DummyProjectAlpha, DummyProjectBeta, TestFile,
|
DummyProjectAlpha, DummyProjectBeta, TestFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
// importing common module.
|
pub mod common;
|
||||||
mod common;
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
|
||||||
@ -242,7 +241,7 @@ async fn creating_loader_fields() {
|
|||||||
USER_USER_PAT,
|
USER_USER_PAT,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(v.fields.get("test_fabric_optional").unwrap(), &json!(555));
|
assert_eq!(&v.fields["test_fabric_optional"], &json!(555));
|
||||||
// - Patch
|
// - Patch
|
||||||
let resp = api
|
let resp = api
|
||||||
.edit_version(
|
.edit_version(
|
||||||
@ -257,7 +256,7 @@ async fn creating_loader_fields() {
|
|||||||
let v = api
|
let v = api
|
||||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(v.fields.get("test_fabric_optional").unwrap(), &json!(555));
|
assert_eq!(&v.fields["test_fabric_optional"], &json!(555));
|
||||||
|
|
||||||
// Simply setting them as expected works
|
// Simply setting them as expected works
|
||||||
// - Create
|
// - Create
|
||||||
@ -286,12 +285,9 @@ async fn creating_loader_fields() {
|
|||||||
USER_USER_PAT,
|
USER_USER_PAT,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(
|
assert_eq!(&v.fields["game_versions"], &json!(["1.20.1", "1.20.2"]));
|
||||||
v.fields.get("game_versions").unwrap(),
|
assert_eq!(&v.fields["singleplayer"], &json!(false));
|
||||||
&json!(["1.20.1", "1.20.2"])
|
assert_eq!(&v.fields["server_only"], &json!(true));
|
||||||
);
|
|
||||||
assert_eq!(v.fields.get("singleplayer").unwrap(), &json!(false));
|
|
||||||
assert_eq!(v.fields.get("server_only").unwrap(), &json!(true));
|
|
||||||
// - Patch
|
// - Patch
|
||||||
let resp = api
|
let resp = api
|
||||||
.edit_version(
|
.edit_version(
|
||||||
@ -308,10 +304,7 @@ async fn creating_loader_fields() {
|
|||||||
let v = api
|
let v = api
|
||||||
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(
|
assert_eq!(&v.fields["game_versions"], &json!(["1.20.1", "1.20.2"]));
|
||||||
v.fields.get("game_versions").unwrap(),
|
|
||||||
&json!(["1.20.1", "1.20.2"])
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now that we've created a version, we need to make sure that the Project's loader fields are updated (aggregate)
|
// Now that we've created a version, we need to make sure that the Project's loader fields are updated (aggregate)
|
||||||
// First, add a new version
|
// First, add a new version
|
||||||
@ -361,23 +354,11 @@ async fn creating_loader_fields() {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
project.fields.get("game_versions").unwrap(),
|
&project.fields["game_versions"],
|
||||||
&[json!("1.20.1"), json!("1.20.2"), json!("1.20.5")]
|
&[json!("1.20.1"), json!("1.20.2"), json!("1.20.5")]
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(project.fields["singleplayer"].contains(&json!(false)));
|
||||||
project
|
assert!(project.fields["singleplayer"].contains(&json!(true)));
|
||||||
.fields
|
|
||||||
.get("singleplayer")
|
|
||||||
.unwrap()
|
|
||||||
.contains(&json!(false))
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
project
|
|
||||||
.fields
|
|
||||||
.get("singleplayer")
|
|
||||||
.unwrap()
|
|
||||||
.contains(&json!(true))
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -533,7 +514,7 @@ async fn test_multi_get_redis_cache() {
|
|||||||
assert_eq!(projects.len(), 10);
|
assert_eq!(projects.len(), 10);
|
||||||
|
|
||||||
// Ensure all 5 modpacks have 'mrpack_loaders', and all 5 mods do not
|
// Ensure all 5 modpacks have 'mrpack_loaders', and all 5 mods do not
|
||||||
for project in projects.iter() {
|
for project in &projects {
|
||||||
if modpacks.contains(project.slug.as_ref().unwrap()) {
|
if modpacks.contains(project.slug.as_ref().unwrap()) {
|
||||||
assert!(project.fields.contains_key("mrpack_loaders"));
|
assert!(project.fields.contains_key("mrpack_loaders"));
|
||||||
} else if mods.contains(project.slug.as_ref().unwrap()) {
|
} else if mods.contains(project.slug.as_ref().unwrap()) {
|
||||||
@ -566,7 +547,7 @@ async fn test_multi_get_redis_cache() {
|
|||||||
assert_eq!(versions.len(), 10);
|
assert_eq!(versions.len(), 10);
|
||||||
|
|
||||||
// Ensure all 5 versions from modpacks have 'mrpack_loaders', and all 5 versions from mods do not
|
// Ensure all 5 versions from modpacks have 'mrpack_loaders', and all 5 versions from mods do not
|
||||||
for version in versions.iter() {
|
for version in &versions {
|
||||||
if version_ids_modpacks.contains(&version.id) {
|
if version_ids_modpacks.contains(&version.id) {
|
||||||
assert!(version.fields.contains_key("mrpack_loaders"));
|
assert!(version.fields.contains_key("mrpack_loaders"));
|
||||||
} else if version_ids_mods.contains(&version.id) {
|
} else if version_ids_mods.contains(&version.id) {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use common::{
|
|||||||
|
|
||||||
use crate::common::api_common::ApiTeams;
|
use crate::common::api_common::ApiTeams;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
pub async fn get_user_notifications_after_team_invitation_returns_notification()
|
pub async fn get_user_notifications_after_team_invitation_returns_notification()
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use common::{
|
|||||||
};
|
};
|
||||||
use labrinth::auth::oauth::TokenResponse;
|
use labrinth::auth::oauth::TokenResponse;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn oauth_flow_happy_path() {
|
async fn oauth_flow_happy_path() {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use labrinth::{
|
|||||||
|
|
||||||
use common::database::USER_USER_ID_PARSED;
|
use common::database::USER_USER_ID_PARSED;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn can_create_edit_get_oauth_client() {
|
async fn can_create_edit_get_oauth_client() {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ use common::{
|
|||||||
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn create_organization() {
|
async fn create_organization() {
|
||||||
@ -583,9 +583,7 @@ async fn add_remove_organization_project_ownership_to_user() {
|
|||||||
.await;
|
.await;
|
||||||
assert_eq!(members.len(), 1);
|
assert_eq!(members.len(), 1);
|
||||||
assert_eq!(members[0].user.id.to_string(), FRIEND_USER_ID);
|
assert_eq!(members[0].user.id.to_string(), FRIEND_USER_ID);
|
||||||
let user_member =
|
assert_eq!(members.iter().filter(|m| m.is_owner).count(), 0);
|
||||||
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
||||||
assert_eq!(user_member.len(), 0);
|
|
||||||
|
|
||||||
// Beta project should have:
|
// Beta project should have:
|
||||||
// - No members
|
// - No members
|
||||||
@ -836,9 +834,7 @@ async fn delete_organization_means_all_projects_to_org_owner() {
|
|||||||
.api
|
.api
|
||||||
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
||||||
.await;
|
.await;
|
||||||
let user_member =
|
assert_eq!(members.iter().filter(|m| m.is_owner).count(), 0);
|
||||||
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
||||||
assert_eq!(user_member.len(), 0);
|
|
||||||
|
|
||||||
// Transfer ownership of zeta organization to FRIEND
|
// Transfer ownership of zeta organization to FRIEND
|
||||||
let resp = test_env
|
let resp = test_env
|
||||||
@ -856,9 +852,7 @@ async fn delete_organization_means_all_projects_to_org_owner() {
|
|||||||
.api
|
.api
|
||||||
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
||||||
.await;
|
.await;
|
||||||
let user_member =
|
assert_eq!(members.iter().filter(|m| m.is_owner).count(), 0);
|
||||||
members.iter().filter(|m| m.is_owner).collect::<Vec<_>>();
|
|
||||||
assert_eq!(user_member.len(), 0);
|
|
||||||
|
|
||||||
// Delete organization
|
// Delete organization
|
||||||
let resp = test_env
|
let resp = test_env
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use serde_json::json;
|
|||||||
|
|
||||||
use crate::common::api_common::AppendsOptionalPat;
|
use crate::common::api_common::AppendsOptionalPat;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
// Full pat test:
|
// Full pat test:
|
||||||
// - create a PAT and ensure it can be used for the scope
|
// - create a PAT and ensure it can be used for the scope
|
||||||
|
|||||||
@ -27,7 +27,7 @@ use labrinth::util::actix::{MultipartSegment, MultipartSegmentData};
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sha1::Digest;
|
use sha1::Digest;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_get_project() {
|
async fn test_get_project() {
|
||||||
|
|||||||
@ -29,7 +29,7 @@ use serde_json::json;
|
|||||||
// - test the function with the PAT with the given scopes
|
// - test the function with the PAT with the given scopes
|
||||||
// - test the function with the PAT with all other scopes
|
// - test the function with the PAT with all other scopes
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
// Test for users, emails, and payout scopes (not user auth scope or notifs)
|
// Test for users, emails, and payout scopes (not user auth scope or notifs)
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
|||||||
@ -14,7 +14,7 @@ use serde_json::json;
|
|||||||
use crate::common::api_common::Api;
|
use crate::common::api_common::Api;
|
||||||
use crate::common::api_common::ApiProject;
|
use crate::common::api_common::ApiProject;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
// TODO: Revisit this wit h the new modify_json in the version maker
|
// TODO: Revisit this wit h the new modify_json in the version maker
|
||||||
// That change here should be able to simplify it vastly
|
// That change here should be able to simplify it vastly
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use common::{
|
|||||||
|
|
||||||
use crate::common::api_common::ApiTags;
|
use crate::common::api_common::ApiTags;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn get_tags() {
|
async fn get_tags() {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
|||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_get_team() {
|
async fn test_get_team() {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use common::{
|
|||||||
environment::with_test_environment_all,
|
environment::with_test_environment_all,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod common;
|
pub mod common;
|
||||||
|
|
||||||
// user GET (permissions, different users)
|
// user GET (permissions, different users)
|
||||||
// users GET
|
// users GET
|
||||||
|
|||||||
@ -219,7 +219,7 @@ async fn version_updates() {
|
|||||||
|
|
||||||
// Add 3 new versions, 1 before, and 2 after, with differing game_version/version_types/loaders
|
// Add 3 new versions, 1 before, and 2 after, with differing game_version/version_types/loaders
|
||||||
let mut update_ids = vec![];
|
let mut update_ids = vec![];
|
||||||
for (version_number, patch_value) in [
|
for (version_number, patch_value) in &[
|
||||||
(
|
(
|
||||||
"0.9.9",
|
"0.9.9",
|
||||||
json!({
|
json!({
|
||||||
@ -241,9 +241,7 @@ async fn version_updates() {
|
|||||||
"version_type": "beta"
|
"version_type": "beta"
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
]
|
] {
|
||||||
.iter()
|
|
||||||
{
|
|
||||||
let version = api
|
let version = api
|
||||||
.add_public_version_deserialized_common(
|
.add_public_version_deserialized_common(
|
||||||
*alpha_project_id_parsed,
|
*alpha_project_id_parsed,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
// importing common module.
|
pub mod common;
|
||||||
mod common;
|
|
||||||
|
|
||||||
// Not all tests expect exactly the same functionality in v2 and v3.
|
// Not all tests expect exactly the same functionality in v2 and v3.
|
||||||
// For example, though we expect the /GET version to return the corresponding project,
|
// For example, though we expect the /GET version to return the corresponding project,
|
||||||
|
|||||||
@ -22,8 +22,7 @@ use labrinth::models::projects::{
|
|||||||
use labrinth::routes::v3::version_file::FileUpdateData;
|
use labrinth::routes::v3::version_file::FileUpdateData;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
// importing common module.
|
pub mod common;
|
||||||
mod common;
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_get_version() {
|
async fn test_get_version() {
|
||||||
@ -164,7 +163,7 @@ async fn version_updates() {
|
|||||||
|
|
||||||
// Add 3 new versions, 1 before, and 2 after, with differing game_version/version_types/loaders
|
// Add 3 new versions, 1 before, and 2 after, with differing game_version/version_types/loaders
|
||||||
let mut update_ids = vec![];
|
let mut update_ids = vec![];
|
||||||
for (version_number, patch_value) in [
|
for (version_number, patch_value) in &[
|
||||||
(
|
(
|
||||||
"0.9.9",
|
"0.9.9",
|
||||||
json!({
|
json!({
|
||||||
@ -186,9 +185,7 @@ async fn version_updates() {
|
|||||||
"version_type": "beta"
|
"version_type": "beta"
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
]
|
] {
|
||||||
.iter()
|
|
||||||
{
|
|
||||||
let version = api
|
let version = api
|
||||||
.add_public_version_deserialized(
|
.add_public_version_deserialized(
|
||||||
*alpha_project_id_parsed,
|
*alpha_project_id_parsed,
|
||||||
|
|||||||
@ -166,9 +166,8 @@ pub async fn test_jre(
|
|||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
major_version: u32,
|
major_version: u32,
|
||||||
) -> crate::Result<bool> {
|
) -> crate::Result<bool> {
|
||||||
let jre = match jre::check_java_at_filepath(&path).await {
|
let Some(jre) = jre::check_java_at_filepath(&path).await else {
|
||||||
Some(jre) => jre,
|
return Ok(false);
|
||||||
None => return Ok(false),
|
|
||||||
};
|
};
|
||||||
let (major, _) = extract_java_majorminor_version(&jre.version)?;
|
let (major, _) = extract_java_majorminor_version(&jre.version)?;
|
||||||
Ok(major == major_version)
|
Ok(major == major_version)
|
||||||
|
|||||||
@ -65,8 +65,7 @@ pub async fn import_curseforge(
|
|||||||
"Curseforge-{}",
|
"Curseforge-{}",
|
||||||
curseforge_instance_folder
|
curseforge_instance_folder
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|a| a.to_string_lossy().to_string())
|
.map_or("Unknown".to_string(), |a| a.to_string_lossy().to_string())
|
||||||
.unwrap_or("Unknown".to_string())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let state = State::get().await?;
|
let state = State::get().await?;
|
||||||
|
|||||||
@ -52,8 +52,7 @@ pub async fn import_gdlauncher(
|
|||||||
"GDLauncher-{}",
|
"GDLauncher-{}",
|
||||||
gdlauncher_instance_folder
|
gdlauncher_instance_folder
|
||||||
.file_name()
|
.file_name()
|
||||||
.map(|a| a.to_string_lossy().to_string())
|
.map_or("Unknown".to_string(), |a| a.to_string_lossy().to_string())
|
||||||
.unwrap_or("Unknown".to_string())
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Re-cache icon
|
// Re-cache icon
|
||||||
|
|||||||
@ -26,6 +26,7 @@ enum MMCInstanceEnum {
|
|||||||
struct MMCInstanceGeneral {
|
struct MMCInstanceGeneral {
|
||||||
pub general: MMCInstance,
|
pub general: MMCInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub struct MMCInstance {
|
pub struct MMCInstance {
|
||||||
@ -144,9 +145,9 @@ pub async fn is_valid_mmc(instance_folder: PathBuf) -> bool {
|
|||||||
let instance_cfg = instance_folder.join("instance.cfg");
|
let instance_cfg = instance_folder.join("instance.cfg");
|
||||||
let mmc_pack = instance_folder.join("mmc-pack.json");
|
let mmc_pack = instance_folder.join("mmc-pack.json");
|
||||||
|
|
||||||
let mmc_pack = match io::read_any_encoding_to_string(&mmc_pack).await {
|
let Ok((mmc_pack, _)) = io::read_any_encoding_to_string(&mmc_pack).await
|
||||||
Ok((mmc_pack, _)) => mmc_pack,
|
else {
|
||||||
Err(_) => return false,
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
load_instance_cfg(&instance_cfg).await.is_ok()
|
load_instance_cfg(&instance_cfg).await.is_ok()
|
||||||
@ -233,7 +234,7 @@ pub async fn import_mmc(
|
|||||||
// Kept separate as we may in the future want to add special handling for modrinth managed packs
|
// Kept separate as we may in the future want to add special handling for modrinth managed packs
|
||||||
import_mmc_unmanaged(profile_path, minecraft_folder, "Imported Modrinth Modpack".to_string(), description, mmc_pack).await?;
|
import_mmc_unmanaged(profile_path, minecraft_folder, "Imported Modrinth Modpack".to_string(), description, mmc_pack).await?;
|
||||||
}
|
}
|
||||||
Some(MMCManagedPackType::Flame) | Some(MMCManagedPackType::ATLauncher) => {
|
Some(MMCManagedPackType::Flame | MMCManagedPackType::ATLauncher) => {
|
||||||
// For flame/atlauncher managed packs
|
// For flame/atlauncher managed packs
|
||||||
// Treat as unmanaged, but with 'minecraft' folder instead of '.minecraft'
|
// Treat as unmanaged, but with 'minecraft' folder instead of '.minecraft'
|
||||||
import_mmc_unmanaged(profile_path, minecraft_folder, "Imported Modpack".to_string(), description, mmc_pack).await?;
|
import_mmc_unmanaged(profile_path, minecraft_folder, "Imported Modpack".to_string(), description, mmc_pack).await?;
|
||||||
|
|||||||
@ -357,9 +357,7 @@ pub async fn set_profile_information(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let game_version = if let Some(game_version) = game_version {
|
let Some(game_version) = game_version else {
|
||||||
game_version
|
|
||||||
} else {
|
|
||||||
return Err(crate::ErrorKind::InputError(
|
return Err(crate::ErrorKind::InputError(
|
||||||
"Pack did not specify Minecraft version".to_string(),
|
"Pack did not specify Minecraft version".to_string(),
|
||||||
)
|
)
|
||||||
@ -393,10 +391,7 @@ pub async fn set_profile_information(
|
|||||||
locked: if !ignore_lock {
|
locked: if !ignore_lock {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
prof.linked_data
|
prof.linked_data.as_ref().is_none_or(|x| x.locked)
|
||||||
.as_ref()
|
|
||||||
.map(|x| x.locked)
|
|
||||||
.unwrap_or(true)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -152,8 +152,7 @@ pub async fn install_zipped_mrpack_files(
|
|||||||
if let Some(env) = project.env {
|
if let Some(env) = project.env {
|
||||||
if env
|
if env
|
||||||
.get(&EnvType::Client)
|
.get(&EnvType::Client)
|
||||||
.map(|x| x == &SideType::Unsupported)
|
.is_some_and(|x| x == &SideType::Unsupported)
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -586,7 +586,7 @@ pub async fn get_pack_export_candidates(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| IOError::with_path(e, &profile_base_dir))?
|
.map_err(|e| IOError::with_path(e, &profile_base_dir))?
|
||||||
{
|
{
|
||||||
let path: PathBuf = entry.path();
|
let path = entry.path();
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
// Two layers of files/folders if its a folder
|
// Two layers of files/folders if its a folder
|
||||||
let mut read_dir = io::read_dir(&path).await?;
|
let mut read_dir = io::read_dir(&path).await?;
|
||||||
@ -595,10 +595,10 @@ pub async fn get_pack_export_candidates(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| IOError::with_path(e, &profile_base_dir))?
|
.map_err(|e| IOError::with_path(e, &profile_base_dir))?
|
||||||
{
|
{
|
||||||
let path: PathBuf = entry.path();
|
path_list.push(pack_get_relative_path(
|
||||||
|
&profile_base_dir,
|
||||||
path_list
|
&entry.path(),
|
||||||
.push(pack_get_relative_path(&profile_base_dir, &path)?);
|
)?);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// One layer of files/folders if its a file
|
// One layer of files/folders if its a file
|
||||||
@ -669,7 +669,7 @@ pub async fn run_credentials(
|
|||||||
if let Some(command) = cmd.next() {
|
if let Some(command) = cmd.next() {
|
||||||
let full_path = get_full_path(&profile.path).await?;
|
let full_path = get_full_path(&profile.path).await?;
|
||||||
let result = Command::new(command)
|
let result = Command::new(command)
|
||||||
.args(cmd.collect::<Vec<&str>>())
|
.args(cmd)
|
||||||
.current_dir(&full_path)
|
.current_dir(&full_path)
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|e| IOError::with_path(e, &full_path))?
|
.map_err(|e| IOError::with_path(e, &full_path))?
|
||||||
@ -881,13 +881,10 @@ pub async fn create_mrpack_json(
|
|||||||
env.insert(EnvType::Client, SideType::Required);
|
env.insert(EnvType::Client, SideType::Required);
|
||||||
env.insert(EnvType::Server, SideType::Required);
|
env.insert(EnvType::Server, SideType::Required);
|
||||||
|
|
||||||
let primary_file =
|
let Some(primary_file) = version.files.first() else {
|
||||||
if let Some(primary_file) = version.files.first() {
|
return Some(Err(crate::ErrorKind::OtherError(format!(
|
||||||
primary_file
|
"No primary file found for mod at: {path}"
|
||||||
} else {
|
))
|
||||||
return Some(Err(crate::ErrorKind::OtherError(
|
|
||||||
format!("No primary file found for mod at: {path}"),
|
|
||||||
)
|
|
||||||
.as_error()));
|
.as_error()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -255,7 +255,7 @@ async fn get_all_worlds_in_profile(
|
|||||||
AttachedWorldData::get_all_for_instance(profile_path, &state.pool)
|
AttachedWorldData::get_all_for_instance(profile_path, &state.pool)
|
||||||
.await?;
|
.await?;
|
||||||
if !attached_data.is_empty() {
|
if !attached_data.is_empty() {
|
||||||
for world in worlds.iter_mut() {
|
for world in &mut worlds {
|
||||||
if let Some(data) = attached_data
|
if let Some(data) = attached_data
|
||||||
.get(&(world.world_type(), world.world_id().to_owned()))
|
.get(&(world.world_type(), world.world_id().to_owned()))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -139,9 +139,7 @@ pub async fn edit_loading(
|
|||||||
// increment refers to by what relative increment to the loading struct's total to update
|
// increment refers to by what relative increment to the loading struct's total to update
|
||||||
// message is the message to display on the loading bar- if None, use the loading bar's default one
|
// message is the message to display on the loading bar- if None, use the loading bar's default one
|
||||||
// By convention, fraction is the fraction of the progress bar that is filled
|
// By convention, fraction is the fraction of the progress bar that is filled
|
||||||
#[allow(unused_variables)]
|
|
||||||
#[tracing::instrument(level = "debug")]
|
#[tracing::instrument(level = "debug")]
|
||||||
|
|
||||||
pub fn emit_loading(
|
pub fn emit_loading(
|
||||||
key: &LoadingBarId,
|
key: &LoadingBarId,
|
||||||
increment_frac: f64,
|
increment_frac: f64,
|
||||||
@ -149,22 +147,13 @@ pub fn emit_loading(
|
|||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let event_state = crate::EventState::get()?;
|
let event_state = crate::EventState::get()?;
|
||||||
|
|
||||||
let mut loading_bar = match event_state.loading_bars.get_mut(&key.0) {
|
let Some(mut loading_bar) = event_state.loading_bars.get_mut(&key.0) else {
|
||||||
Some(f) => f,
|
|
||||||
None => {
|
|
||||||
return Err(EventError::NoLoadingBar(key.0).into());
|
return Err(EventError::NoLoadingBar(key.0).into());
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tick up loading bar
|
// Tick up loading bar
|
||||||
loading_bar.current += increment_frac;
|
loading_bar.current += increment_frac;
|
||||||
let display_frac = loading_bar.current / loading_bar.total;
|
let display_frac = loading_bar.current / loading_bar.total;
|
||||||
let opt_display_frac = if display_frac >= 1.0 {
|
|
||||||
None // by convention, when its done, we submit None
|
|
||||||
// any further updates will be ignored (also sending None)
|
|
||||||
} else {
|
|
||||||
Some(display_frac)
|
|
||||||
};
|
|
||||||
|
|
||||||
if f64::abs(display_frac - loading_bar.last_sent) > 0.005 {
|
if f64::abs(display_frac - loading_bar.last_sent) > 0.005 {
|
||||||
// Emit event to indicatif progress bar
|
// Emit event to indicatif progress bar
|
||||||
@ -187,7 +176,12 @@ pub fn emit_loading(
|
|||||||
.emit(
|
.emit(
|
||||||
"loading",
|
"loading",
|
||||||
LoadingPayload {
|
LoadingPayload {
|
||||||
fraction: opt_display_frac,
|
fraction: if display_frac >= 1.0 {
|
||||||
|
None // by convention, when its done, we submit None
|
||||||
|
// any further updates will be ignored (also sending None)
|
||||||
|
} else {
|
||||||
|
Some(display_frac)
|
||||||
|
},
|
||||||
message: message
|
message: message
|
||||||
.unwrap_or(&loading_bar.message)
|
.unwrap_or(&loading_bar.message)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
@ -197,6 +191,9 @@ pub fn emit_loading(
|
|||||||
)
|
)
|
||||||
.map_err(EventError::from)?;
|
.map_err(EventError::from)?;
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "cli", feature = "tauri")))]
|
||||||
|
let _ = message;
|
||||||
|
|
||||||
loading_bar.last_sent = display_frac;
|
loading_bar.last_sent = display_frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,8 +201,6 @@ pub fn emit_loading(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// emit_warning(message)
|
// emit_warning(message)
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
||||||
#[cfg(feature = "tauri")]
|
#[cfg(feature = "tauri")]
|
||||||
{
|
{
|
||||||
@ -227,8 +222,6 @@ pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
|||||||
// emit_command(CommandPayload::Something { something })
|
// emit_command(CommandPayload::Something { something })
|
||||||
// ie: installing a pack, opening an .mrpack, etc
|
// ie: installing a pack, opening an .mrpack, etc
|
||||||
// Generally used for url deep links and file opens that we want to handle in the frontend
|
// Generally used for url deep links and file opens that we want to handle in the frontend
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
pub async fn emit_command(command: CommandPayload) -> crate::Result<()> {
|
pub async fn emit_command(command: CommandPayload) -> crate::Result<()> {
|
||||||
tracing::debug!("Command: {}", serde_json::to_string(&command)?);
|
tracing::debug!("Command: {}", serde_json::to_string(&command)?);
|
||||||
#[cfg(feature = "tauri")]
|
#[cfg(feature = "tauri")]
|
||||||
|
|||||||
@ -87,9 +87,9 @@ pub fn get_lib_path(
|
|||||||
lib: &str,
|
lib: &str,
|
||||||
allow_not_exist: bool,
|
allow_not_exist: bool,
|
||||||
) -> crate::Result<String> {
|
) -> crate::Result<String> {
|
||||||
let mut path = libraries_path.to_path_buf();
|
let path = libraries_path
|
||||||
|
.to_path_buf()
|
||||||
path.push(get_path_from_artifact(lib)?);
|
.join(get_path_from_artifact(lib)?);
|
||||||
|
|
||||||
if !path.exists() && allow_not_exist {
|
if !path.exists() && allow_not_exist {
|
||||||
return Ok(path.to_string_lossy().to_string());
|
return Ok(path.to_string_lossy().to_string());
|
||||||
|
|||||||
@ -37,12 +37,7 @@ pub async fn download_minecraft(
|
|||||||
let assets_index =
|
let assets_index =
|
||||||
download_assets_index(st, version, Some(loading_bar), force).await?;
|
download_assets_index(st, version, Some(loading_bar), force).await?;
|
||||||
|
|
||||||
let amount = if version
|
let amount = if version.processors.as_ref().is_some_and(|x| !x.is_empty()) {
|
||||||
.processors
|
|
||||||
.as_ref()
|
|
||||||
.map(|x| !x.is_empty())
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
25.0
|
25.0
|
||||||
} else {
|
} else {
|
||||||
40.0
|
40.0
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use daedalus::modded::LoaderVersion;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use st::Profile;
|
use st::Profile;
|
||||||
use std::collections::HashMap;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
@ -137,8 +137,7 @@ pub async fn get_java_version_from_profile(
|
|||||||
let key = version_info
|
let key = version_info
|
||||||
.java_version
|
.java_version
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|it| it.major_version)
|
.map_or(8, |it| it.major_version);
|
||||||
.unwrap_or(8);
|
|
||||||
|
|
||||||
let state = State::get().await?;
|
let state = State::get().await?;
|
||||||
|
|
||||||
@ -253,8 +252,7 @@ pub async fn install_minecraft(
|
|||||||
|
|
||||||
let loader_version_id = loader_version.clone();
|
let loader_version_id = loader_version.clone();
|
||||||
crate::api::profile::edit(&profile.path, |prof| {
|
crate::api::profile::edit(&profile.path, |prof| {
|
||||||
prof.loader_version =
|
prof.loader_version = loader_version_id.clone().map(|x| x.id);
|
||||||
loader_version_id.clone().map(|x| x.id.clone());
|
|
||||||
|
|
||||||
async { Ok(()) }
|
async { Ok(()) }
|
||||||
})
|
})
|
||||||
@ -279,8 +277,7 @@ pub async fn install_minecraft(
|
|||||||
let key = version_info
|
let key = version_info
|
||||||
.java_version
|
.java_version
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|it| it.major_version)
|
.map_or(8, |it| it.major_version);
|
||||||
.unwrap_or(8);
|
|
||||||
let (java_version, set_java) = if let Some(java_version) =
|
let (java_version, set_java) = if let Some(java_version) =
|
||||||
get_java_version_from_profile(profile, &version_info).await?
|
get_java_version_from_profile(profile, &version_info).await?
|
||||||
{
|
{
|
||||||
@ -631,8 +628,7 @@ pub async fn launch_minecraft(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|x| x.get(&LoggingSide::Client)),
|
.and_then(|x| x.get(&LoggingSide::Client)),
|
||||||
)?
|
)?
|
||||||
.into_iter()
|
.into_iter(),
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
)
|
||||||
.arg(version_info.main_class.clone())
|
.arg(version_info.main_class.clone())
|
||||||
.args(
|
.args(
|
||||||
@ -650,8 +646,7 @@ pub async fn launch_minecraft(
|
|||||||
&java_version.architecture,
|
&java_version.architecture,
|
||||||
quick_play_type,
|
quick_play_type,
|
||||||
)?
|
)?
|
||||||
.into_iter()
|
.into_iter(),
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
)
|
||||||
.current_dir(instance_path.clone());
|
.current_dir(instance_path.clone());
|
||||||
|
|
||||||
@ -695,7 +690,7 @@ pub async fn launch_minecraft(
|
|||||||
// check if the regex exists in the file
|
// check if the regex exists in the file
|
||||||
if !re.is_match(&options_string) {
|
if !re.is_match(&options_string) {
|
||||||
// The key was not found in the file, so append it
|
// The key was not found in the file, so append it
|
||||||
options_string.push_str(&format!("\n{key}:{value}"));
|
write!(&mut options_string, "\n{key}:{value}").unwrap();
|
||||||
} else {
|
} else {
|
||||||
let replaced_string = re
|
let replaced_string = re
|
||||||
.replace_all(&options_string, &format!("{key}:{value}"))
|
.replace_all(&options_string, &format!("{key}:{value}"))
|
||||||
@ -715,31 +710,6 @@ pub async fn launch_minecraft(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut censor_strings = HashMap::new();
|
|
||||||
let username = whoami::username();
|
|
||||||
censor_strings
|
|
||||||
.insert(format!("/{username}/"), "/{COMPUTER_USERNAME}/".to_string());
|
|
||||||
censor_strings.insert(
|
|
||||||
format!("\\{username}\\"),
|
|
||||||
"\\{COMPUTER_USERNAME}\\".to_string(),
|
|
||||||
);
|
|
||||||
censor_strings.insert(
|
|
||||||
credentials.access_token.clone(),
|
|
||||||
"{MINECRAFT_ACCESS_TOKEN}".to_string(),
|
|
||||||
);
|
|
||||||
censor_strings.insert(
|
|
||||||
credentials.username.clone(),
|
|
||||||
"{MINECRAFT_USERNAME}".to_string(),
|
|
||||||
);
|
|
||||||
censor_strings.insert(
|
|
||||||
credentials.id.as_simple().to_string(),
|
|
||||||
"{MINECRAFT_UUID}".to_string(),
|
|
||||||
);
|
|
||||||
censor_strings.insert(
|
|
||||||
credentials.id.as_hyphenated().to_string(),
|
|
||||||
"{MINECRAFT_UUID}".to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If in tauri, and the 'minimize on launch' setting is enabled, minimize the window
|
// If in tauri, and the 'minimize on launch' setting is enabled, minimize the window
|
||||||
#[cfg(feature = "tauri")]
|
#[cfg(feature = "tauri")]
|
||||||
{
|
{
|
||||||
|
|||||||
@ -461,8 +461,7 @@ impl CacheValue {
|
|||||||
CacheValue::Team(members) => members
|
CacheValue::Team(members) => members
|
||||||
.iter()
|
.iter()
|
||||||
.next()
|
.next()
|
||||||
.map(|x| x.team_id.as_str())
|
.map_or(DEFAULT_ID, |x| x.team_id.as_str())
|
||||||
.unwrap_or(DEFAULT_ID)
|
|
||||||
.to_string(),
|
.to_string(),
|
||||||
CacheValue::Organization(org) => org.id.clone(),
|
CacheValue::Organization(org) => org.id.clone(),
|
||||||
CacheValue::File(file) => file.hash.clone(),
|
CacheValue::File(file) => file.hash.clone(),
|
||||||
@ -556,7 +555,6 @@ macro_rules! impl_cache_methods {
|
|||||||
$(
|
$(
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
#[tracing::instrument(skip(pool, fetch_semaphore))]
|
#[tracing::instrument(skip(pool, fetch_semaphore))]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn [<get_ $variant:snake>](
|
pub async fn [<get_ $variant:snake>](
|
||||||
id: &str,
|
id: &str,
|
||||||
cache_behaviour: Option<CacheBehaviour>,
|
cache_behaviour: Option<CacheBehaviour>,
|
||||||
@ -568,7 +566,6 @@ macro_rules! impl_cache_methods {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(pool, fetch_semaphore))]
|
#[tracing::instrument(skip(pool, fetch_semaphore))]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn [<get_ $variant:snake _many>](
|
pub async fn [<get_ $variant:snake _many>](
|
||||||
ids: &[&str],
|
ids: &[&str],
|
||||||
cache_behaviour: Option<CacheBehaviour>,
|
cache_behaviour: Option<CacheBehaviour>,
|
||||||
@ -597,7 +594,6 @@ macro_rules! impl_cache_method_singular {
|
|||||||
$(
|
$(
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
#[tracing::instrument(skip(pool, fetch_semaphore))]
|
#[tracing::instrument(skip(pool, fetch_semaphore))]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn [<get_ $variant:snake>] (
|
pub async fn [<get_ $variant:snake>] (
|
||||||
cache_behaviour: Option<CacheBehaviour>,
|
cache_behaviour: Option<CacheBehaviour>,
|
||||||
pool: &SqlitePool,
|
pool: &SqlitePool,
|
||||||
@ -735,18 +731,13 @@ impl CachedEntry {
|
|||||||
|
|
||||||
remaining_keys.retain(|x| {
|
remaining_keys.retain(|x| {
|
||||||
x != &&*row.id
|
x != &&*row.id
|
||||||
&& !row
|
&& !row.alias.as_ref().is_some_and(|y| {
|
||||||
.alias
|
if type_.case_sensitive_alias().unwrap_or(true) {
|
||||||
.as_ref()
|
|
||||||
.map(|y| {
|
|
||||||
if type_.case_sensitive_alias().unwrap_or(true)
|
|
||||||
{
|
|
||||||
x == y
|
x == y
|
||||||
} else {
|
} else {
|
||||||
y.to_lowercase() == x.to_lowercase()
|
y.to_lowercase() == x.to_lowercase()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(data) = parsed_data {
|
if let Some(data) = parsed_data {
|
||||||
@ -991,7 +982,7 @@ impl CachedEntry {
|
|||||||
let key = key.to_string();
|
let key = key.to_string();
|
||||||
|
|
||||||
if let Some(position) = teams.iter().position(|x| {
|
if let Some(position) = teams.iter().position(|x| {
|
||||||
x.first().map(|x| x.team_id == key).unwrap_or(false)
|
x.first().is_some_and(|x| x.team_id == key)
|
||||||
}) {
|
}) {
|
||||||
let team = teams.remove(position);
|
let team = teams.remove(position);
|
||||||
|
|
||||||
|
|||||||
@ -42,9 +42,8 @@ impl DirectoryInfo {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let config_dir = config_dir
|
let config_dir =
|
||||||
.map(PathBuf::from)
|
config_dir.map_or_else(|| settings_dir.clone(), PathBuf::from);
|
||||||
.unwrap_or_else(|| settings_dir.clone());
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
settings_dir,
|
settings_dir,
|
||||||
@ -193,8 +192,7 @@ impl DirectoryInfo {
|
|||||||
let move_dir = settings
|
let move_dir = settings
|
||||||
.custom_dir
|
.custom_dir
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(PathBuf::from)
|
.map_or_else(|| app_dir.clone(), PathBuf::from);
|
||||||
.unwrap_or_else(|| app_dir.clone());
|
|
||||||
|
|
||||||
async fn is_dir_writeable(
|
async fn is_dir_writeable(
|
||||||
new_config_dir: &Path,
|
new_config_dir: &Path,
|
||||||
@ -220,7 +218,7 @@ impl DirectoryInfo {
|
|||||||
|
|
||||||
let disks = sysinfo::Disks::new_with_refreshed_list();
|
let disks = sysinfo::Disks::new_with_refreshed_list();
|
||||||
|
|
||||||
for disk in disks.iter() {
|
for disk in &disks {
|
||||||
if path.starts_with(disk.mount_point()) {
|
if path.starts_with(disk.mount_point()) {
|
||||||
return Ok(Some(disk.available_space()));
|
return Ok(Some(disk.available_space()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -174,7 +174,7 @@ impl FriendsSocket {
|
|||||||
ServerToClientMessage::FriendRequest { from } => {
|
ServerToClientMessage::FriendRequest { from } => {
|
||||||
let _ = emit_friend(FriendPayload::FriendRequest { from }).await;
|
let _ = emit_friend(FriendPayload::FriendRequest { from }).await;
|
||||||
}
|
}
|
||||||
ServerToClientMessage::FriendRequestRejected { .. } => todo!(),
|
ServerToClientMessage::FriendRequestRejected { .. } => {}, // TODO
|
||||||
|
|
||||||
ServerToClientMessage::FriendSocketListening { .. } => {}, // TODO
|
ServerToClientMessage::FriendSocketListening { .. } => {}, // TODO
|
||||||
ServerToClientMessage::FriendSocketStoppedListening { .. } => {}, // TODO
|
ServerToClientMessage::FriendSocketStoppedListening { .. } => {}, // TODO
|
||||||
|
|||||||
@ -29,9 +29,7 @@ where
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let old_launcher_root = if let Some(dir) = default_settings_dir() {
|
let Some(old_launcher_root) = default_settings_dir() else {
|
||||||
dir
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let old_launcher_root_str = old_launcher_root.to_string_lossy().to_string();
|
let old_launcher_root_str = old_launcher_root.to_string_lossy().to_string();
|
||||||
@ -177,12 +175,10 @@ where
|
|||||||
|
|
||||||
let profile_path = entry.path().join("profile.json");
|
let profile_path = entry.path().join("profile.json");
|
||||||
|
|
||||||
let profile = if let Ok(profile) =
|
let Ok(profile) =
|
||||||
read_json::<LegacyProfile>(&profile_path, &io_semaphore)
|
read_json::<LegacyProfile>(&profile_path, &io_semaphore)
|
||||||
.await
|
.await
|
||||||
{
|
else {
|
||||||
profile
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -285,7 +281,7 @@ where
|
|||||||
|
|
||||||
TeamMember {
|
TeamMember {
|
||||||
team_id: x.team_id,
|
team_id: x.team_id,
|
||||||
user: user.clone(),
|
user,
|
||||||
is_owner: x.role == "Owner",
|
is_owner: x.role == "Owner",
|
||||||
role: x.role,
|
role: x.role,
|
||||||
ordering: x.ordering,
|
ordering: x.ordering,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user