You've already forked AstralRinth
forked from didirus/AstralRinth
Run fmt, fix dep route (#312)
This commit is contained in:
@@ -38,14 +38,26 @@ pub enum AuthorizationError {
|
||||
impl actix_web::ResponseError for AuthorizationError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
AuthorizationError::EnvError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthorizationError::SqlxDatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthorizationError::DatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AuthorizationError::EnvError(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
AuthorizationError::SqlxDatabaseError(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
AuthorizationError::DatabaseError(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
AuthorizationError::SerDeError(..) => StatusCode::BAD_REQUEST,
|
||||
AuthorizationError::GithubError(..) => StatusCode::FAILED_DEPENDENCY,
|
||||
AuthorizationError::InvalidCredentialsError => StatusCode::UNAUTHORIZED,
|
||||
AuthorizationError::GithubError(..) => {
|
||||
StatusCode::FAILED_DEPENDENCY
|
||||
}
|
||||
AuthorizationError::InvalidCredentialsError => {
|
||||
StatusCode::UNAUTHORIZED
|
||||
}
|
||||
AuthorizationError::DecodingError(..) => StatusCode::BAD_REQUEST,
|
||||
AuthorizationError::AuthenticationError(..) => StatusCode::UNAUTHORIZED,
|
||||
AuthorizationError::AuthenticationError(..) => {
|
||||
StatusCode::UNAUTHORIZED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +69,13 @@ impl actix_web::ResponseError for AuthorizationError {
|
||||
AuthorizationError::DatabaseError(..) => "database_error",
|
||||
AuthorizationError::SerDeError(..) => "invalid_input",
|
||||
AuthorizationError::GithubError(..) => "github_error",
|
||||
AuthorizationError::InvalidCredentialsError => "invalid_credentials",
|
||||
AuthorizationError::InvalidCredentialsError => {
|
||||
"invalid_credentials"
|
||||
}
|
||||
AuthorizationError::DecodingError(..) => "decoding_error",
|
||||
AuthorizationError::AuthenticationError(..) => "authentication_error",
|
||||
AuthorizationError::AuthenticationError(..) => {
|
||||
"authentication_error"
|
||||
}
|
||||
},
|
||||
description: &self.to_string(),
|
||||
})
|
||||
@@ -174,11 +190,14 @@ pub async fn auth_callback(
|
||||
|
||||
let user = get_github_user_from_token(&*token.access_token).await?;
|
||||
|
||||
let user_result = User::get_from_github_id(user.id, &mut *transaction).await?;
|
||||
let user_result =
|
||||
User::get_from_github_id(user.id, &mut *transaction).await?;
|
||||
match user_result {
|
||||
Some(_) => {}
|
||||
None => {
|
||||
let user_id = crate::database::models::generate_user_id(&mut transaction).await?;
|
||||
let user_id =
|
||||
crate::database::models::generate_user_id(&mut transaction)
|
||||
.await?;
|
||||
|
||||
let mut username_increment: i32 = 0;
|
||||
let mut username = None;
|
||||
|
||||
@@ -58,7 +58,11 @@ pub async fn maven_metadata(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let project_id = params.into_inner().0;
|
||||
let project_data =
|
||||
database::models::Project::get_full_from_slug_or_project_id(&*project_id, &**pool).await?;
|
||||
database::models::Project::get_full_from_slug_or_project_id(
|
||||
&*project_id,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let data = if let Some(data) = project_data {
|
||||
data
|
||||
@@ -119,7 +123,9 @@ fn find_file<'a>(
|
||||
version: &'a QueryVersion,
|
||||
file: &str,
|
||||
) -> Option<&'a QueryFile> {
|
||||
if let Some(selected_file) = version.files.iter().find(|x| x.filename == file) {
|
||||
if let Some(selected_file) =
|
||||
version.files.iter().find(|x| x.filename == file)
|
||||
{
|
||||
return Some(selected_file);
|
||||
}
|
||||
|
||||
@@ -129,7 +135,9 @@ fn find_file<'a>(
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if file == format!("{}-{}.{}", &project_id, &version.version_number, fileext) {
|
||||
if file
|
||||
== format!("{}-{}.{}", &project_id, &version.version_number, fileext)
|
||||
{
|
||||
version
|
||||
.files
|
||||
.iter()
|
||||
@@ -148,7 +156,11 @@ pub async fn version_file(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let (project_id, vnum, file) = params.into_inner();
|
||||
let project_data =
|
||||
database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?;
|
||||
database::models::Project::get_full_from_slug_or_project_id(
|
||||
&project_id,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let project = if let Some(data) = project_data {
|
||||
data
|
||||
@@ -175,9 +187,11 @@ pub async fn version_file(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let version = if let Some(version) =
|
||||
database::models::Version::get_full(database::models::ids::VersionId(vid.id), &**pool)
|
||||
.await?
|
||||
let version = if let Some(version) = database::models::Version::get_full(
|
||||
database::models::ids::VersionId(vid.id),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
version
|
||||
} else {
|
||||
@@ -197,10 +211,12 @@ pub async fn version_file(
|
||||
name: project.inner.title,
|
||||
description: project.inner.description,
|
||||
};
|
||||
return Ok(HttpResponse::Ok()
|
||||
.content_type("text/xml")
|
||||
.body(yaserde::ser::to_string(&respdata).map_err(ApiError::XmlError)?));
|
||||
} else if let Some(selected_file) = find_file(&project_id, &project, &version, &file) {
|
||||
return Ok(HttpResponse::Ok().content_type("text/xml").body(
|
||||
yaserde::ser::to_string(&respdata).map_err(ApiError::XmlError)?,
|
||||
));
|
||||
} else if let Some(selected_file) =
|
||||
find_file(&project_id, &project, &version, &file)
|
||||
{
|
||||
return Ok(HttpResponse::TemporaryRedirect()
|
||||
.append_header(("location", &*selected_file.url))
|
||||
.body(""));
|
||||
@@ -217,7 +233,11 @@ pub async fn version_file_sha1(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let (project_id, vnum, file) = params.into_inner();
|
||||
let project_data =
|
||||
database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?;
|
||||
database::models::Project::get_full_from_slug_or_project_id(
|
||||
&project_id,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let project = if let Some(data) = project_data {
|
||||
data
|
||||
@@ -244,9 +264,11 @@ pub async fn version_file_sha1(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let version = if let Some(version) =
|
||||
database::models::Version::get_full(database::models::ids::VersionId(vid.id), &**pool)
|
||||
.await?
|
||||
let version = if let Some(version) = database::models::Version::get_full(
|
||||
database::models::ids::VersionId(vid.id),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
version
|
||||
} else {
|
||||
@@ -268,7 +290,11 @@ pub async fn version_file_sha512(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let (project_id, vnum, file) = params.into_inner();
|
||||
let project_data =
|
||||
database::models::Project::get_full_from_slug_or_project_id(&project_id, &**pool).await?;
|
||||
database::models::Project::get_full_from_slug_or_project_id(
|
||||
&project_id,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let project = if let Some(data) = project_data {
|
||||
data
|
||||
@@ -295,9 +321,11 @@ pub async fn version_file_sha512(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let version = if let Some(version) =
|
||||
database::models::Version::get_full(database::models::ids::VersionId(vid.id), &**pool)
|
||||
.await?
|
||||
let version = if let Some(version) = database::models::Version::get_full(
|
||||
database::models::ids::VersionId(vid.id),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
version
|
||||
} else {
|
||||
|
||||
@@ -187,38 +187,62 @@ pub enum ApiError {
|
||||
impl actix_web::ResponseError for ApiError {
|
||||
fn status_code(&self) -> actix_web::http::StatusCode {
|
||||
match self {
|
||||
ApiError::EnvError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::DatabaseError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::SqlxDatabaseError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::AuthenticationError(..) => actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
ApiError::CustomAuthenticationError(..) => actix_web::http::StatusCode::UNAUTHORIZED,
|
||||
ApiError::XmlError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::EnvError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::DatabaseError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::SqlxDatabaseError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::AuthenticationError(..) => {
|
||||
actix_web::http::StatusCode::UNAUTHORIZED
|
||||
}
|
||||
ApiError::CustomAuthenticationError(..) => {
|
||||
actix_web::http::StatusCode::UNAUTHORIZED
|
||||
}
|
||||
ApiError::XmlError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::JsonError(..) => actix_web::http::StatusCode::BAD_REQUEST,
|
||||
ApiError::SearchError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::IndexingError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::FileHostingError(..) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::InvalidInputError(..) => actix_web::http::StatusCode::BAD_REQUEST,
|
||||
ApiError::ValidationError(..) => actix_web::http::StatusCode::BAD_REQUEST,
|
||||
ApiError::SearchError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::IndexingError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::FileHostingError(..) => {
|
||||
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
ApiError::InvalidInputError(..) => {
|
||||
actix_web::http::StatusCode::BAD_REQUEST
|
||||
}
|
||||
ApiError::ValidationError(..) => {
|
||||
actix_web::http::StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
actix_web::HttpResponse::build(self.status_code()).json(crate::models::error::ApiError {
|
||||
error: match self {
|
||||
ApiError::EnvError(..) => "environment_error",
|
||||
ApiError::SqlxDatabaseError(..) => "database_error",
|
||||
ApiError::DatabaseError(..) => "database_error",
|
||||
ApiError::AuthenticationError(..) => "unauthorized",
|
||||
ApiError::CustomAuthenticationError(..) => "unauthorized",
|
||||
ApiError::XmlError(..) => "xml_error",
|
||||
ApiError::JsonError(..) => "json_error",
|
||||
ApiError::SearchError(..) => "search_error",
|
||||
ApiError::IndexingError(..) => "indexing_error",
|
||||
ApiError::FileHostingError(..) => "file_hosting_error",
|
||||
ApiError::InvalidInputError(..) => "invalid_input",
|
||||
ApiError::ValidationError(..) => "invalid_input",
|
||||
actix_web::HttpResponse::build(self.status_code()).json(
|
||||
crate::models::error::ApiError {
|
||||
error: match self {
|
||||
ApiError::EnvError(..) => "environment_error",
|
||||
ApiError::SqlxDatabaseError(..) => "database_error",
|
||||
ApiError::DatabaseError(..) => "database_error",
|
||||
ApiError::AuthenticationError(..) => "unauthorized",
|
||||
ApiError::CustomAuthenticationError(..) => "unauthorized",
|
||||
ApiError::XmlError(..) => "xml_error",
|
||||
ApiError::JsonError(..) => "json_error",
|
||||
ApiError::SearchError(..) => "search_error",
|
||||
ApiError::IndexingError(..) => "indexing_error",
|
||||
ApiError::FileHostingError(..) => "file_hosting_error",
|
||||
ApiError::InvalidInputError(..) => "invalid_input",
|
||||
ApiError::ValidationError(..) => "invalid_input",
|
||||
},
|
||||
description: &self.to_string(),
|
||||
},
|
||||
description: &self.to_string(),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,18 @@ pub async fn get_projects(
|
||||
count.count as i64
|
||||
)
|
||||
.fetch_many(&**pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| database::models::ProjectId(m.id))) })
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|m| database::models::ProjectId(m.id)))
|
||||
})
|
||||
.try_collect::<Vec<database::models::ProjectId>>()
|
||||
.await?;
|
||||
|
||||
let projects: Vec<_> = database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(crate::models::projects::Project::from)
|
||||
.collect();
|
||||
let projects: Vec<_> =
|
||||
database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(crate::models::projects::Project::from)
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(projects))
|
||||
}
|
||||
|
||||
@@ -31,8 +31,11 @@ pub async fn notifications_get(
|
||||
.collect();
|
||||
|
||||
let notifications_data: Vec<DBNotification> =
|
||||
database::models::notification_item::Notification::get_many(notification_ids, &**pool)
|
||||
.await?;
|
||||
database::models::notification_item::Notification::get_many(
|
||||
notification_ids,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let notifications: Vec<Notification> = notifications_data
|
||||
.into_iter()
|
||||
@@ -54,7 +57,11 @@ pub async fn notification_get(
|
||||
let id = info.into_inner().0;
|
||||
|
||||
let notification_data =
|
||||
database::models::notification_item::Notification::get(id.into(), &**pool).await?;
|
||||
database::models::notification_item::Notification::get(
|
||||
id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(data) = notification_data {
|
||||
if user.id == data.user_id.into() || user.role.is_mod() {
|
||||
@@ -78,21 +85,29 @@ pub async fn notification_delete(
|
||||
let id = info.into_inner().0;
|
||||
|
||||
let notification_data =
|
||||
database::models::notification_item::Notification::get(id.into(), &**pool).await?;
|
||||
database::models::notification_item::Notification::get(
|
||||
id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(data) = notification_data {
|
||||
if data.user_id == user.id.into() || user.role.is_mod() {
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
database::models::notification_item::Notification::remove(id.into(), &mut transaction)
|
||||
.await?;
|
||||
database::models::notification_item::Notification::remove(
|
||||
id.into(),
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
} else {
|
||||
Err(ApiError::CustomAuthenticationError(
|
||||
"You are not authorized to delete this notification!".to_string(),
|
||||
"You are not authorized to delete this notification!"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
@@ -108,18 +123,23 @@ pub async fn notifications_delete(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let notification_ids = serde_json::from_str::<Vec<NotificationId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
let notification_ids =
|
||||
serde_json::from_str::<Vec<NotificationId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let notifications_data =
|
||||
database::models::notification_item::Notification::get_many(notification_ids, &**pool)
|
||||
.await?;
|
||||
database::models::notification_item::Notification::get_many(
|
||||
notification_ids,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut notifications: Vec<database::models::ids::NotificationId> = Vec::new();
|
||||
let mut notifications: Vec<database::models::ids::NotificationId> =
|
||||
Vec::new();
|
||||
|
||||
for notification in notifications_data {
|
||||
if notification.user_id == user.id.into() || user.role.is_mod() {
|
||||
@@ -127,8 +147,11 @@ pub async fn notifications_delete(
|
||||
}
|
||||
}
|
||||
|
||||
database::models::notification_item::Notification::remove_many(notifications, &mut transaction)
|
||||
.await?;
|
||||
database::models::notification_item::Notification::remove_many(
|
||||
notifications,
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
|
||||
@@ -67,10 +67,14 @@ impl actix_web::ResponseError for CreateError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
CreateError::EnvError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CreateError::SqlxDatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CreateError::SqlxDatabaseError(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
CreateError::DatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CreateError::IndexingError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CreateError::FileHostingError(..) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
CreateError::FileHostingError(..) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
CreateError::SerDeError(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::MultipartError(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::MissingValueError(..) => StatusCode::BAD_REQUEST,
|
||||
@@ -81,7 +85,9 @@ impl actix_web::ResponseError for CreateError {
|
||||
CreateError::InvalidCategory(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::InvalidFileType(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::Unauthorized(..) => StatusCode::UNAUTHORIZED,
|
||||
CreateError::CustomAuthenticationError(..) => StatusCode::UNAUTHORIZED,
|
||||
CreateError::CustomAuthenticationError(..) => {
|
||||
StatusCode::UNAUTHORIZED
|
||||
}
|
||||
CreateError::SlugCollision => StatusCode::BAD_REQUEST,
|
||||
CreateError::ValidationError(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::FileValidationError(..) => StatusCode::BAD_REQUEST,
|
||||
@@ -297,17 +303,21 @@ pub async fn project_create_inner(
|
||||
let cdn_url = dotenv::var("CDN_URL")?;
|
||||
|
||||
// The currently logged in user
|
||||
let current_user = get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
let current_user =
|
||||
get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
|
||||
let project_id: ProjectId = models::generate_project_id(transaction).await?.into();
|
||||
let project_id: ProjectId =
|
||||
models::generate_project_id(transaction).await?.into();
|
||||
|
||||
let project_create_data;
|
||||
let mut versions;
|
||||
let mut versions_map = std::collections::HashMap::new();
|
||||
let mut gallery_urls = Vec::new();
|
||||
|
||||
let all_game_versions = models::categories::GameVersion::list(&mut *transaction).await?;
|
||||
let all_loaders = models::categories::Loader::list(&mut *transaction).await?;
|
||||
let all_game_versions =
|
||||
models::categories::GameVersion::list(&mut *transaction).await?;
|
||||
let all_loaders =
|
||||
models::categories::Loader::list(&mut *transaction).await?;
|
||||
|
||||
{
|
||||
// The first multipart field must be named "data" and contain a
|
||||
@@ -324,9 +334,9 @@ pub async fn project_create_inner(
|
||||
})?;
|
||||
|
||||
let content_disposition = field.content_disposition();
|
||||
let name = content_disposition
|
||||
.get_name()
|
||||
.ok_or_else(|| CreateError::MissingValueError(String::from("Missing content name")))?;
|
||||
let name = content_disposition.get_name().ok_or_else(|| {
|
||||
CreateError::MissingValueError(String::from("Missing content name"))
|
||||
})?;
|
||||
|
||||
if name != "data" {
|
||||
return Err(CreateError::InvalidInput(String::from(
|
||||
@@ -336,19 +346,22 @@ pub async fn project_create_inner(
|
||||
|
||||
let mut data = Vec::new();
|
||||
while let Some(chunk) = field.next().await {
|
||||
data.extend_from_slice(&chunk.map_err(CreateError::MultipartError)?);
|
||||
data.extend_from_slice(
|
||||
&chunk.map_err(CreateError::MultipartError)?,
|
||||
);
|
||||
}
|
||||
let create_data: ProjectCreateData = serde_json::from_slice(&data)?;
|
||||
|
||||
create_data
|
||||
.validate()
|
||||
.map_err(|err| CreateError::InvalidInput(validation_errors_to_string(err, None)))?;
|
||||
create_data.validate().map_err(|err| {
|
||||
CreateError::InvalidInput(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
|
||||
let slug_project_id_option: Option<ProjectId> =
|
||||
serde_json::from_str(&*format!("\"{}\"", create_data.slug)).ok();
|
||||
|
||||
if let Some(slug_project_id) = slug_project_id_option {
|
||||
let slug_project_id: models::ids::ProjectId = slug_project_id.into();
|
||||
let slug_project_id: models::ids::ProjectId =
|
||||
slug_project_id.into();
|
||||
let results = sqlx::query!(
|
||||
"
|
||||
SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)
|
||||
@@ -393,15 +406,17 @@ pub async fn project_create_inner(
|
||||
project_create_data = create_data;
|
||||
}
|
||||
|
||||
let project_type_id =
|
||||
models::ProjectTypeId::get_id(project_create_data.project_type.clone(), &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!(
|
||||
"Project Type {} does not exist.",
|
||||
project_create_data.project_type.clone()
|
||||
))
|
||||
})?;
|
||||
let project_type_id = models::ProjectTypeId::get_id(
|
||||
project_create_data.project_type.clone(),
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!(
|
||||
"Project Type {} does not exist.",
|
||||
project_create_data.project_type.clone()
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut icon_url = None;
|
||||
|
||||
@@ -409,9 +424,9 @@ pub async fn project_create_inner(
|
||||
let mut field: Field = item.map_err(CreateError::MultipartError)?;
|
||||
let content_disposition = field.content_disposition().clone();
|
||||
|
||||
let name = content_disposition
|
||||
.get_name()
|
||||
.ok_or_else(|| CreateError::MissingValueError("Missing content name".to_string()))?;
|
||||
let name = content_disposition.get_name().ok_or_else(|| {
|
||||
CreateError::MissingValueError("Missing content name".to_string())
|
||||
})?;
|
||||
|
||||
let (file_name, file_extension) =
|
||||
super::version_creation::get_name_ext(&content_disposition)?;
|
||||
@@ -454,11 +469,21 @@ pub async fn project_create_inner(
|
||||
|
||||
let hash = sha1::Sha1::from(&data).hexdigest();
|
||||
let (_, file_extension) =
|
||||
super::version_creation::get_name_ext(&content_disposition)?;
|
||||
let content_type = crate::util::ext::get_image_content_type(file_extension)
|
||||
.ok_or_else(|| CreateError::InvalidIconFormat(file_extension.to_string()))?;
|
||||
super::version_creation::get_name_ext(
|
||||
&content_disposition,
|
||||
)?;
|
||||
let content_type =
|
||||
crate::util::ext::get_image_content_type(file_extension)
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidIconFormat(
|
||||
file_extension.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let url = format!("data/{}/images/{}.{}", project_id, hash, file_extension);
|
||||
let url = format!(
|
||||
"data/{}/images/{}.{}",
|
||||
project_id, hash, file_extension
|
||||
);
|
||||
let upload_data = file_host
|
||||
.upload_file(content_type, &url, data.freeze())
|
||||
.await?;
|
||||
@@ -491,7 +516,8 @@ pub async fn project_create_inner(
|
||||
|
||||
// `index` is always valid for these lists
|
||||
let created_version = versions.get_mut(index).unwrap();
|
||||
let version_data = project_create_data.initial_versions.get(index).unwrap();
|
||||
let version_data =
|
||||
project_create_data.initial_versions.get(index).unwrap();
|
||||
|
||||
// Upload the new jar file
|
||||
super::version_creation::upload_file(
|
||||
@@ -529,7 +555,8 @@ pub async fn project_create_inner(
|
||||
}
|
||||
|
||||
// Convert the list of category names to actual categories
|
||||
let mut categories = Vec::with_capacity(project_create_data.categories.len());
|
||||
let mut categories =
|
||||
Vec::with_capacity(project_create_data.categories.len());
|
||||
for category in &project_create_data.categories {
|
||||
let id = models::categories::Category::get_id_project(
|
||||
category,
|
||||
@@ -568,44 +595,58 @@ pub async fn project_create_inner(
|
||||
let status_id = models::StatusId::get_id(&status, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!("Status {} does not exist.", status.clone()))
|
||||
CreateError::InvalidInput(format!(
|
||||
"Status {} does not exist.",
|
||||
status.clone()
|
||||
))
|
||||
})?;
|
||||
let client_side_id =
|
||||
models::SideTypeId::get_id(&project_create_data.client_side, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(
|
||||
"Client side type specified does not exist.".to_string(),
|
||||
)
|
||||
})?;
|
||||
let client_side_id = models::SideTypeId::get_id(
|
||||
&project_create_data.client_side,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(
|
||||
"Client side type specified does not exist.".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let server_side_id =
|
||||
models::SideTypeId::get_id(&project_create_data.server_side, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(
|
||||
"Server side type specified does not exist.".to_string(),
|
||||
)
|
||||
})?;
|
||||
let server_side_id = models::SideTypeId::get_id(
|
||||
&project_create_data.server_side,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(
|
||||
"Server side type specified does not exist.".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let license_id =
|
||||
models::categories::License::get_id(&project_create_data.license_id, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput("License specified does not exist.".to_string())
|
||||
})?;
|
||||
let license_id = models::categories::License::get_id(
|
||||
&project_create_data.license_id,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(
|
||||
"License specified does not exist.".to_string(),
|
||||
)
|
||||
})?;
|
||||
let mut donation_urls = vec![];
|
||||
|
||||
if let Some(urls) = &project_create_data.donation_urls {
|
||||
for url in urls {
|
||||
let platform_id = models::DonationPlatformId::get_id(&url.id, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!(
|
||||
"Donation platform {} does not exist.",
|
||||
url.id.clone()
|
||||
))
|
||||
})?;
|
||||
let platform_id = models::DonationPlatformId::get_id(
|
||||
&url.id,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidInput(format!(
|
||||
"Donation platform {} does not exist.",
|
||||
url.id.clone()
|
||||
))
|
||||
})?;
|
||||
|
||||
donation_urls.push(models::project_item::DonationUrl {
|
||||
project_id: project_id.into(),
|
||||
@@ -695,9 +736,12 @@ pub async fn project_create_inner(
|
||||
|
||||
if status == ProjectStatus::Processing {
|
||||
if let Ok(webhook_url) = dotenv::var("MODERATION_DISCORD_WEBHOOK") {
|
||||
crate::util::webhook::send_discord_webhook(response.clone(), webhook_url)
|
||||
.await
|
||||
.ok();
|
||||
crate::util::webhook::send_discord_webhook(
|
||||
response.clone(),
|
||||
webhook_url,
|
||||
)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,12 +764,13 @@ async fn create_initial_version(
|
||||
)));
|
||||
}
|
||||
|
||||
version_data
|
||||
.validate()
|
||||
.map_err(|err| CreateError::ValidationError(validation_errors_to_string(err, None)))?;
|
||||
version_data.validate().map_err(|err| {
|
||||
CreateError::ValidationError(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
|
||||
// Randomly generate a new id to be used for the version
|
||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||
let version_id: VersionId =
|
||||
models::generate_version_id(transaction).await?.into();
|
||||
|
||||
let game_versions = version_data
|
||||
.game_versions
|
||||
@@ -794,8 +839,15 @@ async fn process_icon_upload(
|
||||
mut field: actix_multipart::Field,
|
||||
cdn_url: &str,
|
||||
) -> Result<String, CreateError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(file_extension) {
|
||||
let data = read_from_field(&mut field, 262144, "Icons must be smaller than 256KiB").await?;
|
||||
if let Some(content_type) =
|
||||
crate::util::ext::get_image_content_type(file_extension)
|
||||
{
|
||||
let data = read_from_field(
|
||||
&mut field,
|
||||
262144,
|
||||
"Icons must be smaller than 256KiB",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let upload_data = file_host
|
||||
.upload_file(
|
||||
|
||||
@@ -39,12 +39,14 @@ pub async fn projects_get(
|
||||
web::Query(ids): web::Query<ProjectIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let project_ids = serde_json::from_str::<Vec<models::ids::ProjectId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
let project_ids =
|
||||
serde_json::from_str::<Vec<models::ids::ProjectId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
|
||||
let projects_data = database::models::Project::get_many_full(project_ids, &**pool).await?;
|
||||
let projects_data =
|
||||
database::models::Project::get_many_full(project_ids, &**pool).await?;
|
||||
|
||||
let user_option = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
@@ -71,7 +73,10 @@ pub async fn project_get(
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_data =
|
||||
database::models::Project::get_full_from_slug_or_project_id(&string, &**pool).await?;
|
||||
database::models::Project::get_full_from_slug_or_project_id(
|
||||
&string, &**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let user_option = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
@@ -96,7 +101,9 @@ pub async fn dependency_list(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get_from_slug_or_project_id(string, &**pool).await?;
|
||||
let result =
|
||||
database::models::Project::get_from_slug_or_project_id(string, &**pool)
|
||||
.await?;
|
||||
|
||||
if let Some(project) = result {
|
||||
let id = project.id;
|
||||
@@ -105,9 +112,10 @@ pub async fn dependency_list(
|
||||
|
||||
let dependencies = sqlx::query!(
|
||||
"
|
||||
SELECT d.dependent_id, d.dependency_id, d.mod_dependency_id
|
||||
SELECT d.dependency_id, vd.mod_id
|
||||
FROM versions v
|
||||
INNER JOIN dependencies d ON d.dependent_id = v.id
|
||||
INNER JOIN versions vd ON d.dependency_id = vd.id
|
||||
WHERE v.mod_id = $1
|
||||
",
|
||||
id as database::models::ProjectId
|
||||
@@ -116,26 +124,24 @@ pub async fn dependency_list(
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|x| {
|
||||
(
|
||||
database::models::VersionId(x.dependent_id),
|
||||
x.dependency_id.map(database::models::VersionId),
|
||||
x.mod_dependency_id.map(database::models::ProjectId),
|
||||
database::models::ProjectId(x.mod_id),
|
||||
)
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<(
|
||||
database::models::VersionId,
|
||||
Option<database::models::VersionId>,
|
||||
Option<database::models::ProjectId>,
|
||||
database::models::ProjectId,
|
||||
)>>()
|
||||
.await?;
|
||||
|
||||
let (projects_result, versions_result) = futures::join!(
|
||||
database::Project::get_many_full(
|
||||
dependencies.iter().map(|x| x.2).flatten().collect(),
|
||||
dependencies.iter().map(|x| x.1).collect(),
|
||||
&**pool,
|
||||
),
|
||||
database::Version::get_many_full(
|
||||
dependencies.iter().map(|x| x.1).flatten().collect(),
|
||||
dependencies.iter().map(|x| x.0).flatten().collect(),
|
||||
&**pool,
|
||||
)
|
||||
);
|
||||
@@ -244,13 +250,15 @@ pub async fn project_edit(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
new_project
|
||||
.validate()
|
||||
.map_err(|err| ApiError::ValidationError(validation_errors_to_string(err, None)))?;
|
||||
new_project.validate().map_err(|err| {
|
||||
ApiError::ValidationError(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
|
||||
let string = info.into_inner().0;
|
||||
let result =
|
||||
database::models::Project::get_full_from_slug_or_project_id(&string, &**pool).await?;
|
||||
let result = database::models::Project::get_full_from_slug_or_project_id(
|
||||
&string, &**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(project_item) = result {
|
||||
let id = project_item.inner.id;
|
||||
@@ -324,11 +332,13 @@ pub async fn project_edit(
|
||||
));
|
||||
}
|
||||
|
||||
if (status == &ProjectStatus::Rejected || status == &ProjectStatus::Approved)
|
||||
if (status == &ProjectStatus::Rejected
|
||||
|| status == &ProjectStatus::Approved)
|
||||
&& !user.role.is_mod()
|
||||
{
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to set this status".to_string(),
|
||||
"You don't have permission to set this status"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -361,7 +371,9 @@ pub async fn project_edit(
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
if let Ok(webhook_url) = dotenv::var("MODERATION_DISCORD_WEBHOOK") {
|
||||
if let Ok(webhook_url) =
|
||||
dotenv::var("MODERATION_DISCORD_WEBHOOK")
|
||||
{
|
||||
crate::util::webhook::send_discord_webhook(
|
||||
Project::from(project_item.clone()),
|
||||
webhook_url,
|
||||
@@ -371,13 +383,16 @@ pub async fn project_edit(
|
||||
}
|
||||
}
|
||||
|
||||
let status_id = database::models::StatusId::get_id(status, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"No database entry for status provided.".to_string(),
|
||||
)
|
||||
})?;
|
||||
let status_id = database::models::StatusId::get_id(
|
||||
status,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"No database entry for status provided.".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -391,12 +406,19 @@ pub async fn project_edit(
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
if project_item.status.is_searchable() && !status.is_searchable() {
|
||||
if project_item.status.is_searchable()
|
||||
&& !status.is_searchable()
|
||||
{
|
||||
delete_from_index(id.into(), config).await?;
|
||||
} else if !project_item.status.is_searchable() && status.is_searchable() {
|
||||
} else if !project_item.status.is_searchable()
|
||||
&& status.is_searchable()
|
||||
{
|
||||
let index_project =
|
||||
crate::search::indexing::local_import::query_one(id, &mut *transaction)
|
||||
.await?;
|
||||
crate::search::indexing::local_import::query_one(
|
||||
id,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
indexing_queue.add(index_project);
|
||||
}
|
||||
@@ -422,14 +444,17 @@ pub async fn project_edit(
|
||||
|
||||
for category in categories {
|
||||
let category_id =
|
||||
database::models::categories::Category::get_id(category, &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Category {} does not exist.",
|
||||
category.clone()
|
||||
))
|
||||
})?;
|
||||
database::models::categories::Category::get_id(
|
||||
category,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Category {} does not exist.",
|
||||
category.clone()
|
||||
))
|
||||
})?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -574,7 +599,8 @@ pub async fn project_edit(
|
||||
|
||||
if results.exists.unwrap_or(true) {
|
||||
return Err(ApiError::InvalidInputError(
|
||||
"Slug collides with other project's id!".to_string(),
|
||||
"Slug collides with other project's id!"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -601,10 +627,12 @@ pub async fn project_edit(
|
||||
));
|
||||
}
|
||||
|
||||
let side_type_id =
|
||||
database::models::SideTypeId::get_id(new_side, &mut *transaction)
|
||||
.await?
|
||||
.expect("No database entry found for side type");
|
||||
let side_type_id = database::models::SideTypeId::get_id(
|
||||
new_side,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.expect("No database entry found for side type");
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -627,10 +655,12 @@ pub async fn project_edit(
|
||||
));
|
||||
}
|
||||
|
||||
let side_type_id =
|
||||
database::models::SideTypeId::get_id(new_side, &mut *transaction)
|
||||
.await?
|
||||
.expect("No database entry found for side type");
|
||||
let side_type_id = database::models::SideTypeId::get_id(
|
||||
new_side,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.expect("No database entry found for side type");
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -653,10 +683,12 @@ pub async fn project_edit(
|
||||
));
|
||||
}
|
||||
|
||||
let license_id =
|
||||
database::models::categories::License::get_id(license, &mut *transaction)
|
||||
.await?
|
||||
.expect("No database entry found for license");
|
||||
let license_id = database::models::categories::License::get_id(
|
||||
license,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.expect("No database entry found for license");
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -690,17 +722,18 @@ pub async fn project_edit(
|
||||
.await?;
|
||||
|
||||
for donation in donations {
|
||||
let platform_id = database::models::DonationPlatformId::get_id(
|
||||
&donation.id,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Platform {} does not exist.",
|
||||
donation.id.clone()
|
||||
))
|
||||
})?;
|
||||
let platform_id =
|
||||
database::models::DonationPlatformId::get_id(
|
||||
&donation.id,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Platform {} does not exist.",
|
||||
donation.id.clone()
|
||||
))
|
||||
})?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -717,7 +750,9 @@ pub async fn project_edit(
|
||||
}
|
||||
|
||||
if let Some(moderation_message) = &new_project.moderation_message {
|
||||
if !user.role.is_mod() && project_item.status != ProjectStatus::Approved {
|
||||
if !user.role.is_mod()
|
||||
&& project_item.status != ProjectStatus::Approved
|
||||
{
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You do not have the permissions to edit the moderation message of this project!"
|
||||
.to_string(),
|
||||
@@ -737,8 +772,12 @@ pub async fn project_edit(
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(moderation_message_body) = &new_project.moderation_message_body {
|
||||
if !user.role.is_mod() && project_item.status != ProjectStatus::Approved {
|
||||
if let Some(moderation_message_body) =
|
||||
&new_project.moderation_message_body
|
||||
{
|
||||
if !user.role.is_mod()
|
||||
&& project_item.status != ProjectStatus::Approved
|
||||
{
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You do not have the permissions to edit the moderation message body of this project!"
|
||||
.to_string(),
|
||||
@@ -805,17 +844,24 @@ pub async fn project_icon_edit(
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
mut payload: web::Payload,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(&*ext.ext) {
|
||||
if let Some(content_type) =
|
||||
crate::util::ext::get_image_content_type(&*ext.ext)
|
||||
{
|
||||
let cdn_url = dotenv::var("CDN_URL")?;
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item =
|
||||
database::models::Project::get_from_slug_or_project_id(string.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
database::models::Project::get_from_slug_or_project_id(
|
||||
string.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
@@ -826,12 +872,15 @@ pub async fn project_icon_edit(
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member.permissions.contains(Permissions::EDIT_DETAILS) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit this project's icon.".to_string(),
|
||||
"You don't have permission to edit this project's icon."
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -844,8 +893,12 @@ pub async fn project_icon_edit(
|
||||
}
|
||||
}
|
||||
|
||||
let bytes =
|
||||
read_from_payload(&mut payload, 262144, "Icons must be smaller than 256KiB").await?;
|
||||
let bytes = read_from_payload(
|
||||
&mut payload,
|
||||
262144,
|
||||
"Icons must be smaller than 256KiB",
|
||||
)
|
||||
.await?;
|
||||
let hash = sha1::Sha1::from(&bytes).hexdigest();
|
||||
let project_id: ProjectId = project_item.id.into();
|
||||
let upload_data = file_host
|
||||
@@ -891,12 +944,16 @@ pub async fn delete_project_icon(
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item =
|
||||
database::models::Project::get_from_slug_or_project_id(string.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let project_item = database::models::Project::get_from_slug_or_project_id(
|
||||
string.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
@@ -907,12 +964,15 @@ pub async fn delete_project_icon(
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member.permissions.contains(Permissions::EDIT_DETAILS) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit this project's icon.".to_string(),
|
||||
"You don't have permission to edit this project's icon."
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -962,20 +1022,28 @@ pub async fn add_gallery_item(
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
mut payload: web::Payload,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(&*ext.ext) {
|
||||
item.validate()
|
||||
.map_err(|err| ApiError::ValidationError(validation_errors_to_string(err, None)))?;
|
||||
if let Some(content_type) =
|
||||
crate::util::ext::get_image_content_type(&*ext.ext)
|
||||
{
|
||||
item.validate().map_err(|err| {
|
||||
ApiError::ValidationError(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
|
||||
let cdn_url = dotenv::var("CDN_URL")?;
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item =
|
||||
database::models::Project::get_from_slug_or_project_id(string.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
database::models::Project::get_from_slug_or_project_id(
|
||||
string.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
@@ -986,12 +1054,15 @@ pub async fn add_gallery_item(
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member.permissions.contains(Permissions::EDIT_DETAILS) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit this project's gallery.".to_string(),
|
||||
"You don't have permission to edit this project's gallery."
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1079,15 +1150,20 @@ pub async fn edit_gallery_item(
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
item.validate()
|
||||
.map_err(|err| ApiError::ValidationError(validation_errors_to_string(err, None)))?;
|
||||
item.validate().map_err(|err| {
|
||||
ApiError::ValidationError(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
|
||||
let project_item =
|
||||
database::models::Project::get_from_slug_or_project_id(string.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let project_item = database::models::Project::get_from_slug_or_project_id(
|
||||
string.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
@@ -1098,12 +1174,15 @@ pub async fn edit_gallery_item(
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member.permissions.contains(Permissions::EDIT_DETAILS) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit this project's gallery.".to_string(),
|
||||
"You don't have permission to edit this project's gallery."
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1203,12 +1282,16 @@ pub async fn delete_gallery_item(
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item =
|
||||
database::models::Project::get_from_slug_or_project_id(string.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let project_item = database::models::Project::get_from_slug_or_project_id(
|
||||
string.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
@@ -1219,12 +1302,15 @@ pub async fn delete_gallery_item(
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member.permissions.contains(Permissions::EDIT_DETAILS) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit this project's gallery.".to_string(),
|
||||
"You don't have permission to edit this project's gallery."
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1280,23 +1366,31 @@ pub async fn project_delete(
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project = database::models::Project::get_from_slug_or_project_id(string.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let project = database::models::Project::get_from_slug_or_project_id(
|
||||
string.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id_project(
|
||||
project.id,
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let team_member =
|
||||
database::models::TeamMember::get_from_user_id_project(
|
||||
project.id,
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member
|
||||
.permissions
|
||||
@@ -1310,7 +1404,9 @@ pub async fn project_delete(
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let result = database::models::Project::remove_full(project.id, &mut transaction).await?;
|
||||
let result =
|
||||
database::models::Project::remove_full(project.id, &mut transaction)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
@@ -1332,11 +1428,14 @@ pub async fn project_follow(
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get_from_slug_or_project_id(string, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let result =
|
||||
database::models::Project::get_from_slug_or_project_id(string, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let user_id: database::models::ids::UserId = user.id.into();
|
||||
let project_id: database::models::ids::ProjectId = result.id;
|
||||
@@ -1397,11 +1496,14 @@ pub async fn project_unfollow(
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get_from_slug_or_project_id(string, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The specified project does not exist!".to_string())
|
||||
})?;
|
||||
let result =
|
||||
database::models::Project::get_from_slug_or_project_id(string, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The specified project does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let user_id: database::models::ids::UserId = user.id.into();
|
||||
let project_id = result.id;
|
||||
@@ -1457,9 +1559,11 @@ pub async fn delete_from_index(
|
||||
id: crate::models::projects::ProjectId,
|
||||
config: web::Data<SearchConfig>,
|
||||
) -> Result<(), meilisearch_sdk::errors::Error> {
|
||||
let client = meilisearch_sdk::client::Client::new(&*config.address, &*config.key);
|
||||
let client =
|
||||
meilisearch_sdk::client::Client::new(&*config.address, &*config.key);
|
||||
|
||||
let indexes: Vec<meilisearch_sdk::indexes::Index> = client.get_indexes().await?;
|
||||
let indexes: Vec<meilisearch_sdk::indexes::Index> =
|
||||
client.get_indexes().await?;
|
||||
for index in indexes {
|
||||
index.delete_document(format!("{}", id)).await?;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::models::ids::{ProjectId, UserId, VersionId};
|
||||
use crate::models::reports::{ItemType, Report};
|
||||
use crate::routes::ApiError;
|
||||
use crate::util::auth::{check_is_moderator_from_headers, get_user_from_headers};
|
||||
use crate::util::auth::{
|
||||
check_is_moderator_from_headers, get_user_from_headers,
|
||||
};
|
||||
use actix_web::{delete, get, post, web, HttpRequest, HttpResponse};
|
||||
use futures::StreamExt;
|
||||
use serde::Deserialize;
|
||||
@@ -23,24 +25,31 @@ pub async fn report_create(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
let current_user =
|
||||
get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
|
||||
let mut bytes = web::BytesMut::new();
|
||||
while let Some(item) = body.next().await {
|
||||
bytes.extend_from_slice(&item.map_err(|_| {
|
||||
ApiError::InvalidInputError("Error while parsing request payload!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"Error while parsing request payload!".to_string(),
|
||||
)
|
||||
})?);
|
||||
}
|
||||
let new_report: CreateReport = serde_json::from_slice(bytes.as_ref())?;
|
||||
|
||||
let id = crate::database::models::generate_report_id(&mut transaction).await?;
|
||||
let id =
|
||||
crate::database::models::generate_report_id(&mut transaction).await?;
|
||||
let report_type = crate::database::models::categories::ReportType::get_id(
|
||||
&*new_report.report_type,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!("Invalid report type: {}", new_report.report_type))
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Invalid report type: {}",
|
||||
new_report.report_type
|
||||
))
|
||||
})?;
|
||||
let mut report = crate::database::models::report_item::Report {
|
||||
id,
|
||||
@@ -56,17 +65,29 @@ pub async fn report_create(
|
||||
match new_report.item_type {
|
||||
ItemType::Project => {
|
||||
report.project_id = Some(
|
||||
serde_json::from_str::<ProjectId>(&*format!("\"{}\"", new_report.item_id))?.into(),
|
||||
serde_json::from_str::<ProjectId>(&*format!(
|
||||
"\"{}\"",
|
||||
new_report.item_id
|
||||
))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ItemType::Version => {
|
||||
report.version_id = Some(
|
||||
serde_json::from_str::<VersionId>(&*format!("\"{}\"", new_report.item_id))?.into(),
|
||||
serde_json::from_str::<VersionId>(&*format!(
|
||||
"\"{}\"",
|
||||
new_report.item_id
|
||||
))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ItemType::User => {
|
||||
report.user_id = Some(
|
||||
serde_json::from_str::<UserId>(&*format!("\"{}\"", new_report.item_id))?.into(),
|
||||
serde_json::from_str::<UserId>(&*format!(
|
||||
"\"{}\"",
|
||||
new_report.item_id
|
||||
))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ItemType::Unknown => {
|
||||
@@ -127,8 +148,10 @@ pub async fn reports(
|
||||
.try_collect::<Vec<crate::database::models::ids::ReportId>>()
|
||||
.await?;
|
||||
|
||||
let query_reports =
|
||||
crate::database::models::report_item::Report::get_many(report_ids, &**pool).await?;
|
||||
let query_reports = crate::database::models::report_item::Report::get_many(
|
||||
report_ids, &**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut reports = Vec::new();
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::ApiError;
|
||||
use crate::database::models;
|
||||
use crate::database::models::categories::{DonationPlatform, License, ProjectType, ReportType};
|
||||
use crate::database::models::categories::{
|
||||
DonationPlatform, License, ProjectType, ReportType,
|
||||
};
|
||||
use crate::util::auth::check_is_admin_from_headers;
|
||||
use actix_web::{delete, get, put, web, HttpRequest, HttpResponse};
|
||||
use models::categories::{Category, GameVersion, Loader};
|
||||
@@ -40,7 +42,9 @@ pub struct CategoryData {
|
||||
// TODO: searching / filtering? Could be used to implement a live
|
||||
// searching category list
|
||||
#[get("category")]
|
||||
pub async fn category_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
pub async fn category_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mut results = Category::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -64,12 +68,16 @@ pub async fn category_create(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let project_type =
|
||||
crate::database::models::ProjectTypeId::get_id(new_category.project_type.clone(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("Specified project type does not exist!".to_string())
|
||||
})?;
|
||||
let project_type = crate::database::models::ProjectTypeId::get_id(
|
||||
new_category.project_type.clone(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"Specified project type does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let _id = Category::builder()
|
||||
.name(&new_category.name)?
|
||||
@@ -90,7 +98,8 @@ pub async fn category_delete(
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let name = category.into_inner().0;
|
||||
let mut transaction = pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
let mut transaction =
|
||||
pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
|
||||
let result = Category::remove(&name, &mut transaction).await?;
|
||||
|
||||
@@ -114,7 +123,9 @@ pub struct LoaderData {
|
||||
}
|
||||
|
||||
#[get("loader")]
|
||||
pub async fn loader_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
pub async fn loader_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mut results = Loader::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -140,13 +151,18 @@ pub async fn loader_create(
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let project_types =
|
||||
ProjectType::get_many_id(&new_loader.supported_project_types, &mut *transaction).await?;
|
||||
let project_types = ProjectType::get_many_id(
|
||||
&new_loader.supported_project_types,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let _id = Loader::builder()
|
||||
.name(&new_loader.name)?
|
||||
.icon(&new_loader.icon)?
|
||||
.supported_project_types(&*project_types.into_iter().map(|x| x.id).collect::<Vec<_>>())?
|
||||
.supported_project_types(
|
||||
&*project_types.into_iter().map(|x| x.id).collect::<Vec<_>>(),
|
||||
)?
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
@@ -164,7 +180,8 @@ pub async fn loader_delete(
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let name = loader.into_inner().0;
|
||||
let mut transaction = pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
let mut transaction =
|
||||
pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
|
||||
let result = Loader::remove(&name, &mut transaction).await?;
|
||||
|
||||
@@ -200,8 +217,11 @@ pub async fn game_version_list(
|
||||
pool: web::Data<PgPool>,
|
||||
query: web::Query<GameVersionQuery>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let results: Vec<GameVersionQueryData> = if query.type_.is_some() || query.major.is_some() {
|
||||
GameVersion::list_filter(query.type_.as_deref(), query.major, &**pool).await?
|
||||
let results: Vec<GameVersionQueryData> = if query.type_.is_some()
|
||||
|| query.major.is_some()
|
||||
{
|
||||
GameVersion::list_filter(query.type_.as_deref(), query.major, &**pool)
|
||||
.await?
|
||||
} else {
|
||||
GameVersion::list(&**pool).await?
|
||||
}
|
||||
@@ -260,7 +280,8 @@ pub async fn game_version_delete(
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let name = game_version.into_inner().0;
|
||||
let mut transaction = pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
let mut transaction =
|
||||
pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
|
||||
let result = GameVersion::remove(&name, &mut transaction).await?;
|
||||
|
||||
@@ -283,7 +304,9 @@ pub struct LicenseQueryData {
|
||||
}
|
||||
|
||||
#[get("license")]
|
||||
pub async fn license_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
pub async fn license_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let results: Vec<LicenseQueryData> = License::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -329,7 +352,8 @@ pub async fn license_delete(
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let name = license.into_inner().0;
|
||||
let mut transaction = pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
let mut transaction =
|
||||
pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
|
||||
let result = License::remove(&name, &mut transaction).await?;
|
||||
|
||||
@@ -352,15 +376,18 @@ pub struct DonationPlatformQueryData {
|
||||
}
|
||||
|
||||
#[get("donation_platform")]
|
||||
pub async fn donation_platform_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
let results: Vec<DonationPlatformQueryData> = DonationPlatform::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| DonationPlatformQueryData {
|
||||
short: x.short,
|
||||
name: x.name,
|
||||
})
|
||||
.collect();
|
||||
pub async fn donation_platform_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let results: Vec<DonationPlatformQueryData> =
|
||||
DonationPlatform::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| DonationPlatformQueryData {
|
||||
short: x.short,
|
||||
name: x.name,
|
||||
})
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
|
||||
@@ -398,7 +425,8 @@ pub async fn donation_platform_delete(
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let name = loader.into_inner().0;
|
||||
let mut transaction = pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
let mut transaction =
|
||||
pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
|
||||
let result = DonationPlatform::remove(&name, &mut transaction).await?;
|
||||
|
||||
@@ -415,7 +443,9 @@ pub async fn donation_platform_delete(
|
||||
}
|
||||
|
||||
#[get("report_type")]
|
||||
pub async fn report_type_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
pub async fn report_type_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let results = ReportType::list(&**pool).await?;
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
}
|
||||
@@ -444,7 +474,8 @@ pub async fn report_type_delete(
|
||||
check_is_admin_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let name = report_type.into_inner().0;
|
||||
let mut transaction = pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
let mut transaction =
|
||||
pool.begin().await.map_err(models::DatabaseError::from)?;
|
||||
|
||||
let result = ReportType::remove(&name, &mut transaction).await?;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::database::models::notification_item::{NotificationActionBuilder, NotificationBuilder};
|
||||
use crate::database::models::notification_item::{
|
||||
NotificationActionBuilder, NotificationBuilder,
|
||||
};
|
||||
use crate::database::models::TeamMember;
|
||||
use crate::models::ids::ProjectId;
|
||||
use crate::models::teams::{Permissions, TeamId};
|
||||
@@ -17,23 +19,33 @@ pub async fn team_members_get_project(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
let project_data =
|
||||
crate::database::models::Project::get_from_slug_or_project_id(string, &**pool).await?;
|
||||
crate::database::models::Project::get_from_slug_or_project_id(
|
||||
string, &**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(project) = project_data {
|
||||
let members_data = TeamMember::get_from_team_full(project.team_id, &**pool).await?;
|
||||
let members_data =
|
||||
TeamMember::get_from_team_full(project.team_id, &**pool).await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
let current_user =
|
||||
get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
if let Some(user) = current_user {
|
||||
let team_member =
|
||||
TeamMember::get_from_user_id(project.team_id, user.id.into(), &**pool)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
let team_member = TeamMember::get_from_user_id(
|
||||
project.team_id,
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
|
||||
if team_member.is_some() {
|
||||
let team_members: Vec<_> = members_data
|
||||
.into_iter()
|
||||
.map(|data| crate::models::teams::TeamMember::from(data, false))
|
||||
.map(|data| {
|
||||
crate::models::teams::TeamMember::from(data, false)
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(HttpResponse::Ok().json(team_members));
|
||||
@@ -59,14 +71,16 @@ pub async fn team_members_get(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
let members_data = TeamMember::get_from_team_full(id.into(), &**pool).await?;
|
||||
let members_data =
|
||||
TeamMember::get_from_team_full(id.into(), &**pool).await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
if let Some(user) = current_user {
|
||||
let team_member = TeamMember::get_from_user_id(id.into(), user.id.into(), &**pool)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
let team_member =
|
||||
TeamMember::get_from_user_id(id.into(), user.id.into(), &**pool)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
|
||||
if team_member.is_some() {
|
||||
let team_members: Vec<_> = members_data
|
||||
@@ -96,8 +110,12 @@ pub async fn join_team(
|
||||
let team_id = info.into_inner().0.into();
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let member =
|
||||
TeamMember::get_from_user_id_pending(team_id, current_user.id.into(), &**pool).await?;
|
||||
let member = TeamMember::get_from_user_id_pending(
|
||||
team_id,
|
||||
current_user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(member) = member {
|
||||
if member.accepted {
|
||||
@@ -153,17 +171,20 @@ pub async fn add_team_member(
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let member = TeamMember::get_from_user_id(team_id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team".to_string(),
|
||||
)
|
||||
})?;
|
||||
let member =
|
||||
TeamMember::get_from_user_id(team_id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !member.permissions.contains(Permissions::MANAGE_INVITES) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to invite users to this team".to_string(),
|
||||
"You don't have permission to invite users to this team"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
if !member.permissions.contains(new_member.permissions) {
|
||||
@@ -191,16 +212,23 @@ pub async fn add_team_member(
|
||||
));
|
||||
} else {
|
||||
return Err(ApiError::InvalidInputError(
|
||||
"There is already a pending member request for this user".to_string(),
|
||||
"There is already a pending member request for this user"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
crate::database::models::User::get(member.user_id, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| ApiError::InvalidInputError("An invalid User ID specified".to_string()))?;
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"An invalid User ID specified".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let new_id = crate::database::models::ids::generate_team_member_id(&mut transaction).await?;
|
||||
let new_id =
|
||||
crate::database::models::ids::generate_team_member_id(&mut transaction)
|
||||
.await?;
|
||||
TeamMember {
|
||||
id: new_id,
|
||||
team_id,
|
||||
@@ -232,11 +260,18 @@ pub async fn add_team_member(
|
||||
"Team invite from {} to join the team for project {}",
|
||||
current_user.username, result.title
|
||||
),
|
||||
link: format!("/{}/{}", result.project_type, ProjectId(result.id as u64)),
|
||||
link: format!(
|
||||
"/{}/{}",
|
||||
result.project_type,
|
||||
ProjectId(result.id as u64)
|
||||
),
|
||||
actions: vec![
|
||||
NotificationActionBuilder {
|
||||
title: "Accept".to_string(),
|
||||
action_route: ("POST".to_string(), format!("team/{}/join", team)),
|
||||
action_route: (
|
||||
"POST".to_string(),
|
||||
format!("team/{}/join", team),
|
||||
),
|
||||
},
|
||||
NotificationActionBuilder {
|
||||
title: "Deny".to_string(),
|
||||
@@ -273,20 +308,24 @@ pub async fn edit_team_member(
|
||||
let user_id = ids.1.into();
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let member = TeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team".to_string(),
|
||||
)
|
||||
})?;
|
||||
let edit_member_db = TeamMember::get_from_user_id_pending(id, user_id, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team".to_string(),
|
||||
)
|
||||
})?;
|
||||
let member =
|
||||
TeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
let edit_member_db =
|
||||
TeamMember::get_from_user_id_pending(id, user_id, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
@@ -298,14 +337,16 @@ pub async fn edit_team_member(
|
||||
|
||||
if !member.permissions.contains(Permissions::EDIT_MEMBER) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team".to_string(),
|
||||
"You don't have permission to edit members of this team"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(new_permissions) = edit_member.permissions {
|
||||
if !member.permissions.contains(new_permissions) {
|
||||
return Err(ApiError::InvalidInputError(
|
||||
"The new permissions have permissions that you don't have".to_string(),
|
||||
"The new permissions have permissions that you don't have"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -346,22 +387,34 @@ pub async fn transfer_ownership(
|
||||
let id = info.into_inner().0;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let member = TeamMember::get_from_user_id(id.into(), current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team".to_string(),
|
||||
)
|
||||
})?;
|
||||
let new_member = TeamMember::get_from_user_id(id.into(), new_owner.user_id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The new owner specified does not exist".to_string())
|
||||
})?;
|
||||
let member = TeamMember::get_from_user_id(
|
||||
id.into(),
|
||||
current_user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
let new_member = TeamMember::get_from_user_id(
|
||||
id.into(),
|
||||
new_owner.user_id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"The new owner specified does not exist".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if member.role != crate::models::teams::OWNER_ROLE {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit the ownership of this team".to_string(),
|
||||
"You don't have permission to edit the ownership of this team"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -409,15 +462,18 @@ pub async fn remove_team_member(
|
||||
let user_id = ids.1.into();
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let member = TeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team".to_string(),
|
||||
)
|
||||
})?;
|
||||
let member =
|
||||
TeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit members of this team"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let delete_member = TeamMember::get_from_user_id_pending(id, user_id, &**pool).await?;
|
||||
let delete_member =
|
||||
TeamMember::get_from_user_id_pending(id, user_id, &**pool).await?;
|
||||
|
||||
if let Some(delete_member) = delete_member {
|
||||
if delete_member.role == crate::models::teams::OWNER_ROLE {
|
||||
@@ -431,7 +487,8 @@ pub async fn remove_team_member(
|
||||
// Members other than the owner can either leave the team, or be
|
||||
// removed by a member with the REMOVE_MEMBER permission.
|
||||
if delete_member.user_id == member.user_id
|
||||
|| (member.permissions.contains(Permissions::REMOVE_MEMBER) && member.accepted)
|
||||
|| (member.permissions.contains(Permissions::REMOVE_MEMBER)
|
||||
&& member.accepted)
|
||||
{
|
||||
TeamMember::delete(id, user_id, &**pool).await?;
|
||||
} else {
|
||||
@@ -440,7 +497,8 @@ pub async fn remove_team_member(
|
||||
));
|
||||
}
|
||||
} else if delete_member.user_id == member.user_id
|
||||
|| (member.permissions.contains(Permissions::MANAGE_INVITES) && member.accepted)
|
||||
|| (member.permissions.contains(Permissions::MANAGE_INVITES)
|
||||
&& member.accepted)
|
||||
{
|
||||
// This is a pending invite rather than a member, so the
|
||||
// user being invited or team members with the MANAGE_INVITES
|
||||
@@ -448,7 +506,8 @@ pub async fn remove_team_member(
|
||||
TeamMember::delete(id, user_id, &**pool).await?;
|
||||
} else {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You do not have permission to cancel a team invite".to_string(),
|
||||
"You do not have permission to cancel a team invite"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
|
||||
@@ -20,9 +20,11 @@ pub async fn forge_updates(
|
||||
|
||||
let (id,) = info.into_inner();
|
||||
|
||||
let project = database::models::Project::get_full_from_slug_or_project_id(&id, &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| ApiError::InvalidInputError(ERROR.to_string()))?;
|
||||
let project = database::models::Project::get_full_from_slug_or_project_id(
|
||||
&id, &**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| ApiError::InvalidInputError(ERROR.to_string()))?;
|
||||
|
||||
let user_option = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
@@ -38,7 +40,8 @@ pub async fn forge_updates(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut versions = database::models::Version::get_many_full(version_ids, &**pool).await?;
|
||||
let mut versions =
|
||||
database::models::Version::get_many_full(version_ids, &**pool).await?;
|
||||
versions.sort_by(|a, b| b.date_published.cmp(&a.date_published));
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -48,7 +51,11 @@ pub async fn forge_updates(
|
||||
}
|
||||
|
||||
let mut response = ForgeUpdates {
|
||||
homepage: format!("{}/mod/{}", dotenv::var("SITE_URL").unwrap_or_default(), id),
|
||||
homepage: format!(
|
||||
"{}/mod/{}",
|
||||
dotenv::var("SITE_URL").unwrap_or_default(),
|
||||
id
|
||||
),
|
||||
promos: HashMap::new(),
|
||||
};
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ pub async fn user_auth_get(
|
||||
req: HttpRequest,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
Ok(HttpResponse::Ok()
|
||||
.json(get_user_from_headers(req.headers(), &mut *pool.acquire().await?).await?))
|
||||
Ok(HttpResponse::Ok().json(
|
||||
get_user_from_headers(req.headers(), &mut *pool.acquire().await?)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -41,7 +43,8 @@ pub async fn users_get(
|
||||
|
||||
let users_data = User::get_many(user_ids, &**pool).await?;
|
||||
|
||||
let users: Vec<crate::models::users::User> = users_data.into_iter().map(From::from).collect();
|
||||
let users: Vec<crate::models::users::User> =
|
||||
users_data.into_iter().map(From::from).collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(users))
|
||||
}
|
||||
@@ -52,7 +55,8 @@ pub async fn user_get(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
let id_option: Option<UserId> = serde_json::from_str(&*format!("\"{}\"", string)).ok();
|
||||
let id_option: Option<UserId> =
|
||||
serde_json::from_str(&*format!("\"{}\"", string)).ok();
|
||||
|
||||
let mut user_data;
|
||||
|
||||
@@ -82,9 +86,11 @@ pub async fn projects_list(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
let user_id: UserId = id.into();
|
||||
@@ -93,17 +99,24 @@ pub async fn projects_list(
|
||||
if current_user.role.is_mod() || current_user.id == user_id {
|
||||
User::get_projects_private(id, &**pool).await?
|
||||
} else {
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool).await?
|
||||
User::get_projects(
|
||||
id,
|
||||
ProjectStatus::Approved.as_str(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
} else {
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool).await?
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool)
|
||||
.await?
|
||||
};
|
||||
|
||||
let response: Vec<_> = crate::database::Project::get_many_full(project_data, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Project::from)
|
||||
.collect();
|
||||
let response: Vec<_> =
|
||||
crate::database::Project::get_many_full(project_data, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Project::from)
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
} else {
|
||||
@@ -152,13 +165,15 @@ pub async fn user_edit(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
new_user
|
||||
.validate()
|
||||
.map_err(|err| ApiError::ValidationError(validation_errors_to_string(err, None)))?;
|
||||
new_user.validate().map_err(|err| {
|
||||
ApiError::ValidationError(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
let user_id: UserId = id.into();
|
||||
@@ -168,8 +183,10 @@ pub async fn user_edit(
|
||||
|
||||
if let Some(username) = &new_user.username {
|
||||
let user_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(username, &**pool)
|
||||
.await?;
|
||||
crate::database::models::User::get_id_from_username_or_id(
|
||||
username, &**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if user_option.is_none() {
|
||||
sqlx::query!(
|
||||
@@ -282,19 +299,23 @@ pub async fn user_icon_edit(
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
mut payload: web::Payload,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(&*ext.ext) {
|
||||
if let Some(content_type) =
|
||||
crate::util::ext::get_image_content_type(&*ext.ext)
|
||||
{
|
||||
let cdn_url = dotenv::var("CDN_URL")?;
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
if user.id != id.into() && !user.role.is_mod() {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to edit this user's icon.".to_string(),
|
||||
"You don't have permission to edit this user's icon."
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -322,8 +343,12 @@ pub async fn user_icon_edit(
|
||||
}
|
||||
}
|
||||
|
||||
let bytes =
|
||||
read_from_payload(&mut payload, 2097152, "Icons must be smaller than 2MiB").await?;
|
||||
let bytes = read_from_payload(
|
||||
&mut payload,
|
||||
2097152,
|
||||
"Icons must be smaller than 2MiB",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let upload_data = file_host
|
||||
.upload_file(
|
||||
@@ -374,9 +399,11 @@ pub async fn user_delete(
|
||||
removal_type: web::Query<RemovalType>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
if !user.role.is_mod() && user.id != id.into() {
|
||||
@@ -389,9 +416,15 @@ pub async fn user_delete(
|
||||
|
||||
let result;
|
||||
if &*removal_type.removal_type == "full" {
|
||||
result = crate::database::models::User::remove_full(id, &mut transaction).await?;
|
||||
result = crate::database::models::User::remove_full(
|
||||
id,
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
result = crate::database::models::User::remove(id, &mut transaction).await?;
|
||||
result =
|
||||
crate::database::models::User::remove(id, &mut transaction)
|
||||
.await?;
|
||||
};
|
||||
|
||||
transaction.commit().await?;
|
||||
@@ -413,9 +446,11 @@ pub async fn user_follows(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
if !user.role.is_mod() && user.id != id.into() {
|
||||
@@ -441,11 +476,12 @@ pub async fn user_follows(
|
||||
.try_collect::<Vec<crate::database::models::ProjectId>>()
|
||||
.await?;
|
||||
|
||||
let projects: Vec<_> = crate::database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Project::from)
|
||||
.collect();
|
||||
let projects: Vec<_> =
|
||||
crate::database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Project::from)
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(projects))
|
||||
} else {
|
||||
@@ -460,9 +496,11 @@ pub async fn user_notifications(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
if !user.role.is_mod() && user.id != id.into() {
|
||||
|
||||
@@ -30,15 +30,18 @@ pub async fn get_mods(
|
||||
count.count as i64
|
||||
)
|
||||
.fetch_many(&**pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| database::models::ProjectId(m.id))) })
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|m| database::models::ProjectId(m.id)))
|
||||
})
|
||||
.try_collect::<Vec<database::models::ProjectId>>()
|
||||
.await?;
|
||||
|
||||
let projects: Vec<_> = database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Project::from)
|
||||
.collect();
|
||||
let projects: Vec<_> =
|
||||
database::Project::get_many_full(project_ids, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Project::from)
|
||||
.collect();
|
||||
|
||||
Ok(HttpResponse::Ok().json(projects))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models::projects::SearchRequest;
|
||||
use crate::routes::project_creation::{project_create_inner, undo_uploads, CreateError};
|
||||
use crate::routes::project_creation::{
|
||||
project_create_inner, undo_uploads, CreateError,
|
||||
};
|
||||
use crate::routes::projects::ProjectIds;
|
||||
use crate::routes::ApiError;
|
||||
use crate::search::{search_for_project, SearchConfig, SearchError};
|
||||
@@ -89,12 +91,14 @@ pub async fn mods_get(
|
||||
ids: web::Query<ProjectIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let project_ids = serde_json::from_str::<Vec<models::ids::ProjectId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
let project_ids =
|
||||
serde_json::from_str::<Vec<models::ids::ProjectId>>(&*ids.ids)?
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
|
||||
let projects_data = database::models::Project::get_many_full(project_ids, &**pool).await?;
|
||||
let projects_data =
|
||||
database::models::Project::get_many_full(project_ids, &**pool).await?;
|
||||
|
||||
let user_option = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ use crate::models::ids::ReportId;
|
||||
use crate::models::projects::{ProjectId, VersionId};
|
||||
use crate::models::users::UserId;
|
||||
use crate::routes::ApiError;
|
||||
use crate::util::auth::{check_is_moderator_from_headers, get_user_from_headers};
|
||||
use crate::util::auth::{
|
||||
check_is_moderator_from_headers, get_user_from_headers,
|
||||
};
|
||||
use actix_web::web;
|
||||
use actix_web::{get, post, HttpRequest, HttpResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -56,24 +58,31 @@ pub async fn report_create(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
let current_user =
|
||||
get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
|
||||
let mut bytes = web::BytesMut::new();
|
||||
while let Some(item) = body.next().await {
|
||||
bytes.extend_from_slice(&item.map_err(|_| {
|
||||
ApiError::InvalidInputError("Error while parsing request payload!".to_string())
|
||||
ApiError::InvalidInputError(
|
||||
"Error while parsing request payload!".to_string(),
|
||||
)
|
||||
})?);
|
||||
}
|
||||
let new_report: CreateReport = serde_json::from_slice(bytes.as_ref())?;
|
||||
|
||||
let id = crate::database::models::generate_report_id(&mut transaction).await?;
|
||||
let id =
|
||||
crate::database::models::generate_report_id(&mut transaction).await?;
|
||||
let report_type = crate::database::models::categories::ReportType::get_id(
|
||||
&*new_report.report_type,
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(format!("Invalid report type: {}", new_report.report_type))
|
||||
ApiError::InvalidInputError(format!(
|
||||
"Invalid report type: {}",
|
||||
new_report.report_type
|
||||
))
|
||||
})?;
|
||||
let mut report = crate::database::models::report_item::Report {
|
||||
id,
|
||||
@@ -89,17 +98,29 @@ pub async fn report_create(
|
||||
match new_report.item_type {
|
||||
ItemType::Mod => {
|
||||
report.project_id = Some(
|
||||
serde_json::from_str::<ProjectId>(&*format!("\"{}\"", new_report.item_id))?.into(),
|
||||
serde_json::from_str::<ProjectId>(&*format!(
|
||||
"\"{}\"",
|
||||
new_report.item_id
|
||||
))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ItemType::Version => {
|
||||
report.version_id = Some(
|
||||
serde_json::from_str::<VersionId>(&*format!("\"{}\"", new_report.item_id))?.into(),
|
||||
serde_json::from_str::<VersionId>(&*format!(
|
||||
"\"{}\"",
|
||||
new_report.item_id
|
||||
))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ItemType::User => {
|
||||
report.user_id = Some(
|
||||
serde_json::from_str::<UserId>(&*format!("\"{}\"", new_report.item_id))?.into(),
|
||||
serde_json::from_str::<UserId>(&*format!(
|
||||
"\"{}\"",
|
||||
new_report.item_id
|
||||
))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ItemType::Unknown => {
|
||||
@@ -160,8 +181,10 @@ pub async fn reports(
|
||||
.try_collect::<Vec<crate::database::models::ids::ReportId>>()
|
||||
.await?;
|
||||
|
||||
let query_reports =
|
||||
crate::database::models::report_item::Report::get_many(report_ids, &**pool).await?;
|
||||
let query_reports = crate::database::models::report_item::Report::get_many(
|
||||
report_ids, &**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut reports = Vec::new();
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::database::models::categories::{Category, GameVersion, Loader, ProjectType};
|
||||
use crate::database::models::categories::{
|
||||
Category, GameVersion, Loader, ProjectType,
|
||||
};
|
||||
use crate::routes::ApiError;
|
||||
use crate::util::auth::check_is_admin_from_headers;
|
||||
use actix_web::{get, put, web};
|
||||
@@ -8,7 +10,9 @@ use sqlx::PgPool;
|
||||
const DEFAULT_ICON: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>"#;
|
||||
|
||||
#[get("category")]
|
||||
pub async fn category_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
pub async fn category_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let results = Category::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -28,11 +32,16 @@ pub async fn category_create(
|
||||
|
||||
let name = category.into_inner().0;
|
||||
|
||||
let project_type = crate::database::models::ProjectTypeId::get_id("mod".to_string(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("Specified project type does not exist!".to_string())
|
||||
})?;
|
||||
let project_type = crate::database::models::ProjectTypeId::get_id(
|
||||
"mod".to_string(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"Specified project type does not exist!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let _id = Category::builder()
|
||||
.name(&name)?
|
||||
@@ -45,7 +54,9 @@ pub async fn category_create(
|
||||
}
|
||||
|
||||
#[get("loader")]
|
||||
pub async fn loader_list(pool: web::Data<PgPool>) -> Result<HttpResponse, ApiError> {
|
||||
pub async fn loader_list(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let results = Loader::list(&**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -67,12 +78,16 @@ pub async fn loader_create(
|
||||
let name = loader.into_inner().0;
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let project_types = ProjectType::get_many_id(&["mod".to_string()], &mut *transaction).await?;
|
||||
let project_types =
|
||||
ProjectType::get_many_id(&["mod".to_string()], &mut *transaction)
|
||||
.await?;
|
||||
|
||||
let _id = Loader::builder()
|
||||
.name(&name)?
|
||||
.icon(DEFAULT_ICON)?
|
||||
.supported_project_types(&*project_types.into_iter().map(|x| x.id).collect::<Vec<_>>())?
|
||||
.supported_project_types(
|
||||
&*project_types.into_iter().map(|x| x.id).collect::<Vec<_>>(),
|
||||
)?
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
@@ -92,11 +107,15 @@ pub async fn game_version_list(
|
||||
query: web::Query<GameVersionQueryData>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if query.type_.is_some() || query.major.is_some() {
|
||||
let results = GameVersion::list_filter(query.type_.as_deref(), query.major, &**pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.version)
|
||||
.collect::<Vec<String>>();
|
||||
let results = GameVersion::list_filter(
|
||||
query.type_.as_deref(),
|
||||
query.major,
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|x| x.version)
|
||||
.collect::<Vec<String>>();
|
||||
Ok(HttpResponse::Ok().json(results))
|
||||
} else {
|
||||
let results = GameVersion::list(&**pool)
|
||||
|
||||
@@ -29,18 +29,20 @@ pub async fn team_members_get(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
let members_data =
|
||||
crate::database::models::TeamMember::get_from_team(id.into(), &**pool).await?;
|
||||
crate::database::models::TeamMember::get_from_team(id.into(), &**pool)
|
||||
.await?;
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
if let Some(user) = current_user {
|
||||
let team_member = crate::database::models::TeamMember::get_from_user_id(
|
||||
id.into(),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
let team_member =
|
||||
crate::database::models::TeamMember::get_from_user_id(
|
||||
id.into(),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?;
|
||||
|
||||
if team_member.is_some() {
|
||||
let team_members: Vec<TeamMember> = members_data
|
||||
|
||||
@@ -15,9 +15,11 @@ pub async fn mods_list(
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await.ok();
|
||||
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
let user_id: UserId = id.into();
|
||||
@@ -26,10 +28,16 @@ pub async fn mods_list(
|
||||
if current_user.role.is_mod() || current_user.id == user_id {
|
||||
User::get_projects_private(id, &**pool).await?
|
||||
} else {
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool).await?
|
||||
User::get_projects(
|
||||
id,
|
||||
ProjectStatus::Approved.as_str(),
|
||||
&**pool,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
} else {
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool).await?
|
||||
User::get_projects(id, ProjectStatus::Approved.as_str(), &**pool)
|
||||
.await?
|
||||
};
|
||||
|
||||
let response = project_data
|
||||
@@ -50,9 +58,11 @@ pub async fn user_follows(
|
||||
pool: web::Data<PgPool>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id_option =
|
||||
crate::database::models::User::get_id_from_username_or_id(&*info.into_inner().0, &**pool)
|
||||
.await?;
|
||||
let id_option = crate::database::models::User::get_id_from_username_or_id(
|
||||
&*info.into_inner().0,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(id) = id_option {
|
||||
if !user.role.is_mod() && user.id != id.into() {
|
||||
@@ -71,7 +81,9 @@ pub async fn user_follows(
|
||||
id as crate::database::models::ids::UserId,
|
||||
)
|
||||
.fetch_many(&**pool)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| ProjectId(m.mod_id as u64))) })
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|m| ProjectId(m.mod_id as u64)))
|
||||
})
|
||||
.try_collect::<Vec<ProjectId>>()
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::database::models;
|
||||
use crate::database::models::notification_item::NotificationBuilder;
|
||||
use crate::database::models::version_item::{VersionBuilder, VersionFileBuilder};
|
||||
use crate::database::models::version_item::{
|
||||
VersionBuilder, VersionFileBuilder,
|
||||
};
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models::projects::{
|
||||
Dependency, GameVersion, Loader, ProjectId, Version, VersionFile, VersionId, VersionType,
|
||||
Dependency, GameVersion, Loader, ProjectId, Version, VersionFile,
|
||||
VersionId, VersionType,
|
||||
};
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::routes::project_creation::{CreateError, UploadedFile};
|
||||
@@ -74,8 +77,11 @@ pub async fn version_create(
|
||||
.await;
|
||||
|
||||
if result.is_err() {
|
||||
let undo_result =
|
||||
super::project_creation::undo_uploads(&***file_host, &uploaded_files).await;
|
||||
let undo_result = super::project_creation::undo_uploads(
|
||||
&***file_host,
|
||||
&uploaded_files,
|
||||
)
|
||||
.await;
|
||||
let rollback_result = transaction.rollback().await;
|
||||
|
||||
if let Err(e) = undo_result {
|
||||
@@ -103,25 +109,30 @@ async fn version_create_inner(
|
||||
let mut initial_version_data = None;
|
||||
let mut version_builder = None;
|
||||
|
||||
let all_game_versions = models::categories::GameVersion::list(&mut *transaction).await?;
|
||||
let all_loaders = models::categories::Loader::list(&mut *transaction).await?;
|
||||
let all_game_versions =
|
||||
models::categories::GameVersion::list(&mut *transaction).await?;
|
||||
let all_loaders =
|
||||
models::categories::Loader::list(&mut *transaction).await?;
|
||||
|
||||
let user = get_user_from_headers(req.headers(), &mut *transaction).await?;
|
||||
|
||||
while let Some(item) = payload.next().await {
|
||||
let mut field: Field = item.map_err(CreateError::MultipartError)?;
|
||||
let content_disposition = field.content_disposition().clone();
|
||||
let name = content_disposition
|
||||
.get_name()
|
||||
.ok_or_else(|| CreateError::MissingValueError("Missing content name".to_string()))?;
|
||||
let name = content_disposition.get_name().ok_or_else(|| {
|
||||
CreateError::MissingValueError("Missing content name".to_string())
|
||||
})?;
|
||||
|
||||
if name == "data" {
|
||||
let mut data = Vec::new();
|
||||
while let Some(chunk) = field.next().await {
|
||||
data.extend_from_slice(&chunk.map_err(CreateError::MultipartError)?);
|
||||
data.extend_from_slice(
|
||||
&chunk.map_err(CreateError::MultipartError)?,
|
||||
);
|
||||
}
|
||||
|
||||
let version_create_data: InitialVersionData = serde_json::from_slice(&data)?;
|
||||
let version_create_data: InitialVersionData =
|
||||
serde_json::from_slice(&data)?;
|
||||
initial_version_data = Some(version_create_data);
|
||||
let version_create_data = initial_version_data.as_ref().unwrap();
|
||||
if version_create_data.project_id.is_none() {
|
||||
@@ -131,10 +142,13 @@ async fn version_create_inner(
|
||||
}
|
||||
|
||||
version_create_data.validate().map_err(|err| {
|
||||
CreateError::ValidationError(validation_errors_to_string(err, None))
|
||||
CreateError::ValidationError(validation_errors_to_string(
|
||||
err, None,
|
||||
))
|
||||
})?;
|
||||
|
||||
let project_id: models::ProjectId = version_create_data.project_id.unwrap().into();
|
||||
let project_id: models::ProjectId =
|
||||
version_create_data.project_id.unwrap().into();
|
||||
|
||||
// Ensure that the project this version is being added to exists
|
||||
let results = sqlx::query!(
|
||||
@@ -162,7 +176,8 @@ async fn version_create_inner(
|
||||
|
||||
if results.exists.unwrap_or(true) {
|
||||
return Err(CreateError::InvalidInput(
|
||||
"A version with that version_number already exists".to_string(),
|
||||
"A version with that version_number already exists"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -176,7 +191,8 @@ async fn version_create_inner(
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload this version!".to_string(),
|
||||
"You don't have permission to upload this version!"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -185,11 +201,13 @@ async fn version_create_inner(
|
||||
.contains(Permissions::UPLOAD_VERSION)
|
||||
{
|
||||
return Err(CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload this version!".to_string(),
|
||||
"You don't have permission to upload this version!"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||
let version_id: VersionId =
|
||||
models::generate_version_id(transaction).await?.into();
|
||||
|
||||
let project_type = sqlx::query!(
|
||||
"
|
||||
@@ -210,7 +228,9 @@ async fn version_create_inner(
|
||||
all_game_versions
|
||||
.iter()
|
||||
.find(|y| y.version == x.0)
|
||||
.ok_or_else(|| CreateError::InvalidGameVersion(x.0.clone()))
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidGameVersion(x.0.clone())
|
||||
})
|
||||
.map(|y| y.id)
|
||||
})
|
||||
.collect::<Result<Vec<models::GameVersionId>, CreateError>>()?;
|
||||
@@ -222,7 +242,9 @@ async fn version_create_inner(
|
||||
all_loaders
|
||||
.iter()
|
||||
.find(|y| {
|
||||
y.loader == x.0 && y.supported_project_types.contains(&project_type)
|
||||
y.loader == x.0
|
||||
&& y.supported_project_types
|
||||
.contains(&project_type)
|
||||
})
|
||||
.ok_or_else(|| CreateError::InvalidLoader(x.0.clone()))
|
||||
.map(|y| y.id)
|
||||
@@ -261,7 +283,9 @@ async fn version_create_inner(
|
||||
}
|
||||
|
||||
let version = version_builder.as_mut().ok_or_else(|| {
|
||||
CreateError::InvalidInput(String::from("`data` field must come before file fields"))
|
||||
CreateError::InvalidInput(String::from(
|
||||
"`data` field must come before file fields",
|
||||
))
|
||||
})?;
|
||||
|
||||
let project_type = sqlx::query!(
|
||||
@@ -276,9 +300,9 @@ async fn version_create_inner(
|
||||
.await?
|
||||
.name;
|
||||
|
||||
let version_data = initial_version_data
|
||||
.clone()
|
||||
.ok_or_else(|| CreateError::InvalidInput("`data` field is required".to_string()))?;
|
||||
let version_data = initial_version_data.clone().ok_or_else(|| {
|
||||
CreateError::InvalidInput("`data` field is required".to_string())
|
||||
})?;
|
||||
|
||||
upload_file(
|
||||
&mut field,
|
||||
@@ -300,10 +324,12 @@ async fn version_create_inner(
|
||||
.await?;
|
||||
}
|
||||
|
||||
let version_data = initial_version_data
|
||||
.ok_or_else(|| CreateError::InvalidInput("`data` field is required".to_string()))?;
|
||||
let builder = version_builder
|
||||
.ok_or_else(|| CreateError::InvalidInput("`data` field is required".to_string()))?;
|
||||
let version_data = initial_version_data.ok_or_else(|| {
|
||||
CreateError::InvalidInput("`data` field is required".to_string())
|
||||
})?;
|
||||
let builder = version_builder.ok_or_else(|| {
|
||||
CreateError::InvalidInput("`data` field is required".to_string())
|
||||
})?;
|
||||
|
||||
if builder.files.is_empty() {
|
||||
return Err(CreateError::InvalidInput(
|
||||
@@ -433,8 +459,11 @@ pub async fn upload_file_to_version(
|
||||
.await;
|
||||
|
||||
if result.is_err() {
|
||||
let undo_result =
|
||||
super::project_creation::undo_uploads(&***file_host, &uploaded_files).await;
|
||||
let undo_result = super::project_creation::undo_uploads(
|
||||
&***file_host,
|
||||
&uploaded_files,
|
||||
)
|
||||
.await;
|
||||
let rollback_result = transaction.rollback().await;
|
||||
|
||||
if let Err(e) = undo_result {
|
||||
@@ -477,21 +506,26 @@ async fn upload_file_to_version_inner(
|
||||
}
|
||||
};
|
||||
|
||||
let team_member =
|
||||
models::TeamMember::get_from_user_id_version(version_id, user.id.into(), &mut *transaction)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload files to this version!".to_string(),
|
||||
)
|
||||
})?;
|
||||
let team_member = models::TeamMember::get_from_user_id_version(
|
||||
version_id,
|
||||
user.id.into(),
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload files to this version!"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member
|
||||
.permissions
|
||||
.contains(Permissions::UPLOAD_VERSION)
|
||||
{
|
||||
return Err(CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload files to this version!".to_string(),
|
||||
"You don't have permission to upload files to this version!"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -510,19 +544,22 @@ async fn upload_file_to_version_inner(
|
||||
.await?
|
||||
.name;
|
||||
|
||||
let all_game_versions = models::categories::GameVersion::list(&mut *transaction).await?;
|
||||
let all_game_versions =
|
||||
models::categories::GameVersion::list(&mut *transaction).await?;
|
||||
|
||||
while let Some(item) = payload.next().await {
|
||||
let mut field: Field = item.map_err(CreateError::MultipartError)?;
|
||||
let content_disposition = field.content_disposition().clone();
|
||||
let name = content_disposition
|
||||
.get_name()
|
||||
.ok_or_else(|| CreateError::MissingValueError("Missing content name".to_string()))?;
|
||||
let name = content_disposition.get_name().ok_or_else(|| {
|
||||
CreateError::MissingValueError("Missing content name".to_string())
|
||||
})?;
|
||||
|
||||
if name == "data" {
|
||||
let mut data = Vec::new();
|
||||
while let Some(chunk) = field.next().await {
|
||||
data.extend_from_slice(&chunk.map_err(CreateError::MultipartError)?);
|
||||
data.extend_from_slice(
|
||||
&chunk.map_err(CreateError::MultipartError)?,
|
||||
);
|
||||
}
|
||||
let file_data: InitialFileData = serde_json::from_slice(&data)?;
|
||||
// TODO: currently no data here, but still required
|
||||
@@ -532,7 +569,9 @@ async fn upload_file_to_version_inner(
|
||||
}
|
||||
|
||||
let _file_data = initial_file_data.as_ref().ok_or_else(|| {
|
||||
CreateError::InvalidInput(String::from("`data` field must come before file fields"))
|
||||
CreateError::InvalidInput(String::from(
|
||||
"`data` field must come before file fields",
|
||||
))
|
||||
})?;
|
||||
|
||||
upload_file(
|
||||
@@ -596,7 +635,9 @@ pub async fn upload_file(
|
||||
let (file_name, file_extension) = get_name_ext(content_disposition)?;
|
||||
|
||||
let content_type = crate::util::ext::project_file_type(file_extension)
|
||||
.ok_or_else(|| CreateError::InvalidFileType(file_extension.to_string()))?;
|
||||
.ok_or_else(|| {
|
||||
CreateError::InvalidFileType(file_extension.to_string())
|
||||
})?;
|
||||
|
||||
let data = read_from_field(
|
||||
field, 100 * (1 << 20),
|
||||
@@ -619,7 +660,8 @@ pub async fn upload_file(
|
||||
|
||||
if exists {
|
||||
return Err(CreateError::InvalidInput(
|
||||
"Duplicate files are not allowed to be uploaded to Modrinth!".to_string(),
|
||||
"Duplicate files are not allowed to be uploaded to Modrinth!"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -683,9 +725,9 @@ pub async fn upload_file(
|
||||
pub fn get_name_ext(
|
||||
content_disposition: &actix_web::http::header::ContentDisposition,
|
||||
) -> Result<(&str, &str), CreateError> {
|
||||
let file_name = content_disposition
|
||||
.get_filename()
|
||||
.ok_or_else(|| CreateError::MissingValueError("Missing content file name".to_string()))?;
|
||||
let file_name = content_disposition.get_filename().ok_or_else(|| {
|
||||
CreateError::MissingValueError("Missing content file name".to_string())
|
||||
})?;
|
||||
let file_extension = if let Some(last_period) = file_name.rfind('.') {
|
||||
file_name.get((last_period + 1)..).unwrap_or("")
|
||||
} else {
|
||||
|
||||
@@ -128,25 +128,28 @@ pub async fn delete_file(
|
||||
|
||||
if let Some(row) = result {
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id_version(
|
||||
database::models::ids::VersionId(row.version_id),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to delete this file!".to_string(),
|
||||
let team_member =
|
||||
database::models::TeamMember::get_from_user_id_version(
|
||||
database::models::ids::VersionId(row.version_id),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
})?;
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to delete this file!"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member
|
||||
.permissions
|
||||
.contains(Permissions::DELETE_VERSION)
|
||||
{
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to delete this file!".to_string(),
|
||||
"You don't have permission to delete this file!"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -167,7 +170,8 @@ pub async fn delete_file(
|
||||
|
||||
if files.len() < 2 {
|
||||
return Err(ApiError::InvalidInputError(
|
||||
"Versions must have at least one file uploaded to them".to_string(),
|
||||
"Versions must have at least one file uploaded to them"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -269,7 +273,9 @@ pub async fn get_update_from_hash(
|
||||
.await?;
|
||||
|
||||
if let Some(version_id) = version_ids.last() {
|
||||
let version_data = database::models::Version::get_full(*version_id, &**pool).await?;
|
||||
let version_data =
|
||||
database::models::Version::get_full(*version_id, &**pool)
|
||||
.await?;
|
||||
|
||||
ok_or_not_found::<QueryVersion, Version>(version_data)
|
||||
} else {
|
||||
@@ -436,12 +442,15 @@ pub async fn update_files(
|
||||
}
|
||||
}
|
||||
|
||||
let versions = database::models::Version::get_many_full(version_ids, &**pool).await?;
|
||||
let versions =
|
||||
database::models::Version::get_many_full(version_ids, &**pool).await?;
|
||||
|
||||
let mut response = HashMap::new();
|
||||
|
||||
for row in &result {
|
||||
if let Some(version) = versions.iter().find(|x| x.id.0 == row.version_id) {
|
||||
if let Some(version) =
|
||||
versions.iter().find(|x| x.id.0 == row.version_id)
|
||||
{
|
||||
response.insert(
|
||||
hex::encode(&row.hash),
|
||||
models::projects::Version::from(version.clone()),
|
||||
|
||||
Reference in New Issue
Block a user