Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions apps/labrinth/src/auth/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use crate::models::projects::{
use crate::models::users::User;
use crate::queue::file_scan::get_files_missing_attribution;
use crate::routes::ApiError;
use crate::util::error::ApiContext as _;
use crate::util::error::Context as _;
use futures::TryStreamExt;
use itertools::Itertools;
use xredis::RedisPool;
Expand Down Expand Up @@ -91,7 +93,8 @@ pub async fn filter_visible_projects(
pool,
hide_unlisted,
)
.await?;
.await
.wrap_api_err("filtering visible project ids")?;
projects.retain(|x| filtered_project_ids.contains(&x.inner.id));
Ok(projects.into_iter().map(|x| x.into()).collect())
}
Expand Down Expand Up @@ -128,7 +131,8 @@ pub async fn filter_visible_project_ids(
if !check_projects.is_empty() {
return_projects.extend(
filter_enlisted_projects_ids(check_projects, user_option, pool)
.await?,
.await
.wrap_api_err("filtering enlisted projects ids")?,
);
}

Expand Down Expand Up @@ -178,7 +182,8 @@ pub async fn filter_enlisted_projects_ids(
}
})
.try_collect::<Vec<()>>()
.await?;
.await
.wrap_internal_err("fetching query results from database")?;
}
Ok(return_projects)
}
Expand Down Expand Up @@ -218,7 +223,8 @@ pub async fn filter_visible_versions(
pool,
redis,
)
.await?;
.await
.wrap_api_err("filtering visible version ids")?;
versions.retain(|x| filtered_version_ids.contains(&x.inner.id));

let version_ids: Vec<_> = versions.iter().map(|v| v.inner.id).collect();
Expand Down Expand Up @@ -265,10 +271,9 @@ impl ValidateAuthorized for models::DBOAuthClient {
return if user.role.is_mod() || user.id == self.created_by.into() {
Ok(())
} else {
Err(ApiError::CustomAuthentication(
"You don't have sufficient permissions to interact with this OAuth application"
.to_string(),
))
Err(ApiError::Auth(eyre::eyre!(
"You don't have sufficient permissions to interact with this OAuth application",
)))
};
}

Expand All @@ -292,20 +297,23 @@ pub async fn filter_visible_version_ids(
// Get visible projects- ones we are allowed to see public versions for.
let visible_project_ids = filter_visible_project_ids(
DBProject::get_many_ids(&project_ids, pool, redis)
.await?
.await
.wrap_api_err("fetching projects for visibility filtering")?
.iter()
.map(|x| &x.inner)
.collect(),
user_option,
pool,
false,
)
.await?;
.await
.wrap_api_err("filtering visible project IDs")?;

// Then, get enlisted versions (Versions that are a part of a project we are a member of)
let enlisted_version_ids =
filter_enlisted_version_ids(versions.clone(), user_option, pool, redis)
.await?;
.await
.wrap_api_err("filtering enlisted version ids")?;

let version_ids: Vec<_> = versions.iter().map(|v| v.id).collect();
let withheld_versions = get_files_missing_attribution(pool, &version_ids)
Expand Down Expand Up @@ -346,14 +354,16 @@ pub async fn filter_enlisted_version_ids(
// Get enlisted projects- ones we are allowed to see hidden versions for.
let authorized_project_ids = filter_enlisted_projects_ids(
DBProject::get_many_ids(&project_ids, pool, redis)
.await?
.await
.wrap_api_err("fetching projects for membership filtering")?
.iter()
.map(|x| &x.inner)
.collect(),
user_option,
pool,
)
.await?;
.await
.wrap_api_err("filtering projects by membership")?;

for version in versions {
if user_option.as_ref().is_some_and(|x| x.role.is_mod())
Expand Down Expand Up @@ -428,7 +438,8 @@ pub async fn is_visible_organization(
) -> Result<bool, ApiError> {
let members =
DBTeamMember::get_from_team_full(organization.team_id, pool, redis)
.await?;
.await
.wrap_internal_err("fetching team members from database")?;

// This is meant to match the same projects as the `Project::is_searchable` method, but we're not using
// it here because that'd entail pulling in all projects for the organization
Expand All @@ -437,7 +448,7 @@ pub async fn is_visible_organization(
organization.id as database::models::ids::DBOrganizationId
)
.fetch_optional(pool)
.await?
.await.wrap_internal_err("checking organization for searchable projects")?
.flatten()
.unwrap_or(false);

Expand Down
25 changes: 20 additions & 5 deletions apps/labrinth/src/clickhouse/fetch.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::util::error::Context as _;
use std::sync::Arc;

use crate::{models::ids::ProjectId, routes::ApiError};
Expand Down Expand Up @@ -47,7 +48,10 @@
.bind(end_date.timestamp())
.bind(projects.iter().map(|x| x.0).collect::<Vec<_>>());

Ok(query.fetch_all().await?)
Ok(query

Check failure on line 51 in apps/labrinth/src/clickhouse/fetch.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

enclosing `Ok` and `?` operator are unneeded
.fetch_all()
.await
.wrap_internal_err("querying database for `fetch_playtimes`")?)
}

// Fetches views as a Vec of ReturnViews
Expand Down Expand Up @@ -77,7 +81,10 @@
.bind(end_date.timestamp())
.bind(projects.iter().map(|x| x.0).collect::<Vec<_>>());

Ok(query.fetch_all().await?)
Ok(query

Check failure on line 84 in apps/labrinth/src/clickhouse/fetch.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

enclosing `Ok` and `?` operator are unneeded
.fetch_all()
.await
.wrap_internal_err("querying database for `fetch_views`")?)
}

// Fetches downloads as a Vec of ReturnDownloads
Expand Down Expand Up @@ -106,7 +113,10 @@
.bind(end_date.timestamp())
.bind(projects.iter().map(|x| x.0).collect::<Vec<_>>());

Ok(query.fetch_all().await?)
Ok(query

Check failure on line 116 in apps/labrinth/src/clickhouse/fetch.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

enclosing `Ok` and `?` operator are unneeded
.fetch_all()
.await
.wrap_internal_err("querying database for `fetch_downloads`")?)
}

pub async fn fetch_countries_downloads(
Expand All @@ -133,7 +143,9 @@
.bind(end_date.timestamp())
.bind(projects.iter().map(|x| x.0).collect::<Vec<_>>());

Ok(query.fetch_all().await?)
Ok(query.fetch_all().await.wrap_internal_err(

Check failure on line 146 in apps/labrinth/src/clickhouse/fetch.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

enclosing `Ok` and `?` operator are unneeded
"querying database for `fetch_countries_downloads`",
)?)
}

pub async fn fetch_countries_views(
Expand All @@ -160,5 +172,8 @@
.bind(end_date.timestamp())
.bind(projects.iter().map(|x| x.0).collect::<Vec<_>>());

Ok(query.fetch_all().await?)
Ok(query

Check failure on line 175 in apps/labrinth/src/clickhouse/fetch.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

enclosing `Ok` and `?` operator are unneeded
.fetch_all()
.await
.wrap_internal_err("querying database for `fetch_countries_views`")?)
}
27 changes: 21 additions & 6 deletions apps/labrinth/src/database/models/notifications_template_item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::database::models::DatabaseError;
use crate::models::v3::notifications::{NotificationChannel, NotificationType};
use crate::routes::ApiError;
use crate::util::error::ApiContext as _;
use crate::util::error::Context as _;
use serde::{Deserialize, Serialize};
use xredis::RedisPool;

Expand Down Expand Up @@ -123,27 +125,40 @@ where
html: String,
}

let mut redis_conn = redis.connect().await?;
let mut redis_conn = redis
.connect()
.await
.wrap_internal_err("connecting to Redis")?;
let redis_key = redis_conn
.key()
.metadata(TEMPLATES_DYNAMIC_HTML_NAMESPACE, key);
if let Some(body) =
redis_conn.get_deserialized::<HtmlBody>(&redis_key).await?
if let Some(body) = redis_conn
.get_deserialized::<HtmlBody>(&redis_key)
.await
.wrap_internal_err("fetching cached data from Redis")?
{
return Ok(body.html);
}

drop(redis_conn);

let cached = HtmlBody { html: get().await? };
let mut redis_conn = redis.connect().await?;
let cached = HtmlBody {
html: get()
.await
.wrap_api_err("generating notification template HTML")?,
};
let mut redis_conn = redis
.connect()
.await
.wrap_internal_err("connecting to Redis")?;
let redis_key = redis_conn
.key()
.metadata(TEMPLATES_DYNAMIC_HTML_NAMESPACE, key);

redis_conn
.set_serialized(&redis_key, &cached, Some(HTML_DATA_CACHE_EXPIRY))
.await?;
.await
.wrap_internal_err("storing cached data in Redis")?;

Ok(cached.html)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::database::models::ids::{DBProductId, DBProductPriceId};
use crate::models::billing::ProductMetadata;
use crate::routes::ApiError;
use crate::util::error::Context as _;

pub struct DBProductsTaxIdentifier {
pub id: i32,
Expand All @@ -18,7 +19,8 @@ impl DBProductsTaxIdentifier {
product_id.0,
)
.fetch_optional(exec)
.await?;
.await
.wrap_internal_err("querying database for `get_product`")?;

Ok(maybe_row.map(|row| DBProductsTaxIdentifier {
id: row.id,
Expand All @@ -41,7 +43,8 @@ impl DBProductsTaxIdentifier {
price_id.0,
)
.fetch_optional(exec)
.await?;
.await
.wrap_internal_err("querying database for `get_price`")?;

Ok(maybe_row.map(|row| DBProductsTaxIdentifier {
id: row.id,
Expand Down Expand Up @@ -73,7 +76,7 @@ pub async fn product_info_by_product_price_id(
product_price_id.0 as i64,
)
.fetch_optional(exec)
.await?;
.await.wrap_internal_err("querying database for `product_info_by_product_price_id`")?;

match maybe_row {
None => Ok(None),
Expand All @@ -83,7 +86,8 @@ pub async fn product_info_by_product_price_id(
tax_processor_id: row.tax_processor_id,
product_id: DBProductId(row.product_id),
},
product_metadata: serde_json::from_value(row.product_metadata)?,
product_metadata: serde_json::from_value(row.product_metadata)
.wrap_request_err("deserializing JSON data")?,
})),
}
}
8 changes: 4 additions & 4 deletions apps/labrinth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,16 +328,16 @@ pub fn app_data_config(
labrinth_config: LabrinthConfig,
) {
cfg.app_data(web::FormConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
routes::ApiError::Request(eyre::eyre!("{err}")).into()
}))
.app_data(web::PathConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
routes::ApiError::Request(eyre::eyre!("{err}")).into()
}))
.app_data(web::QueryConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
routes::ApiError::Request(eyre::eyre!("{err}")).into()
}))
.app_data(web::JsonConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
routes::ApiError::Request(eyre::eyre!("{err}")).into()
}))
.app_data(web::Data::new(labrinth_config.redis_pool.clone()))
.app_data(web::Data::new(labrinth_config.pool.clone()))
Expand Down
21 changes: 10 additions & 11 deletions apps/labrinth/src/models/v3/moderation_notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use crate::routes::ApiError;
use crate::util::error::Context as _;

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct ModerationNote {
Expand Down Expand Up @@ -37,9 +38,9 @@ pub struct PatchModerationNote {
impl PatchModerationNote {
pub fn validate_not_empty(&self) -> Result<(), ApiError> {
if self.notes.is_none() && self.user_rating.is_none() {
return Err(ApiError::InvalidInput(
"must specify `notes` or `user_rating`".to_string(),
));
return Err(ApiError::Request(eyre::eyre!(
"must specify `notes` or `user_rating`",
)));
}

Ok(())
Expand All @@ -53,16 +54,14 @@ pub fn parse_if_match_header(
return Ok(None);
};

let value = value.to_str().map_err(|_| {
ApiError::InvalidInput(
"`if-match` header must be a valid integer".to_string(),
)
})?;
let value = value.to_str().wrap_request_err(
"`if-match` header must be a valid integer".to_string(),
)?;

Some(value.parse::<i32>().map_err(|_| {
ApiError::InvalidInput(
"`if-match` header must be a valid integer".to_string(),
)
ApiError::Request(eyre::eyre!(
"`if-match` header must be a valid integer",
))
}))
.transpose()
}
4 changes: 2 additions & 2 deletions apps/labrinth/src/models/v3/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,8 @@ impl NotificationDeliveryStatus {
NotificationDeliveryStatus::Delivered => Ok(()),
NotificationDeliveryStatus::SkippedPreferences |
NotificationDeliveryStatus::SkippedDefault |
NotificationDeliveryStatus::Pending => Err(ApiError::InvalidInput("An error occurred while sending an email to your email address. Please try again later.".to_owned())),
NotificationDeliveryStatus::PermanentlyFailed => Err(ApiError::InvalidInput("This email address doesn't exist! Please try another one.".to_owned())),
NotificationDeliveryStatus::Pending => Err(ApiError::Request(eyre::eyre!("An error occurred while sending an email to your email address. Please try again later.".to_owned()))),
NotificationDeliveryStatus::PermanentlyFailed => Err(ApiError::Request(eyre::eyre!("This email address doesn't exist! Please try another one.".to_owned()))),
}
}

Expand Down
Loading
Loading