You've already forked AstralRinth
forked from didirus/AstralRinth
@@ -1,8 +1,9 @@
|
||||
use crate::auth::{get_user_from_headers, is_authorized_version};
|
||||
use crate::database::models::project_item::QueryProject;
|
||||
use crate::database::models::version_item::{QueryFile, QueryVersion};
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::{ProjectId, VersionId};
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use crate::{auth::is_authorized, database};
|
||||
use actix_web::{get, route, web, HttpRequest, HttpResponse};
|
||||
@@ -68,7 +69,7 @@ pub async fn maven_metadata(
|
||||
params: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let project_id = params.into_inner().0;
|
||||
let project_data = database::models::Project::get(&project_id, &**pool, &redis).await?;
|
||||
@@ -79,9 +80,16 @@ pub async fn maven_metadata(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if !is_authorized(&data.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
@@ -190,7 +198,7 @@ pub async fn version_file(
|
||||
params: web::Path<(String, String, String)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let (project_id, vnum, file) = params.into_inner();
|
||||
let project_data = database::models::Project::get(&project_id, &**pool, &redis).await?;
|
||||
@@ -201,9 +209,16 @@ pub async fn version_file(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
@@ -274,7 +289,7 @@ pub async fn version_file_sha1(
|
||||
params: web::Path<(String, String, String)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let (project_id, vnum, file) = params.into_inner();
|
||||
let project_data = database::models::Project::get(&project_id, &**pool, &redis).await?;
|
||||
@@ -285,9 +300,16 @@ pub async fn version_file_sha1(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
@@ -332,7 +354,7 @@ pub async fn version_file_sha512(
|
||||
params: web::Path<(String, String, String)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let (project_id, vnum, file) = params.into_inner();
|
||||
let project_data = database::models::Project::get(&project_id, &**pool, &redis).await?;
|
||||
@@ -343,9 +365,16 @@ pub async fn version_file_sha512(
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
};
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
|
||||
@@ -6,8 +6,9 @@ use sqlx::PgPool;
|
||||
|
||||
use crate::auth::{filter_authorized_versions, get_user_from_headers, is_authorized};
|
||||
use crate::database;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::VersionType;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
|
||||
use super::ApiError;
|
||||
|
||||
@@ -21,7 +22,7 @@ pub async fn forge_updates(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
const ERROR: &str = "The specified project does not exist!";
|
||||
|
||||
@@ -31,9 +32,16 @@ pub async fn forge_updates(
|
||||
.await?
|
||||
.ok_or_else(|| ApiError::InvalidInput(ERROR.to_string()))?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
return Err(ApiError::InvalidInput(ERROR.to_string()));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
mod admin;
|
||||
mod moderation;
|
||||
mod notifications;
|
||||
mod pats;
|
||||
pub(crate) mod project_creation;
|
||||
mod projects;
|
||||
mod reports;
|
||||
@@ -22,6 +21,7 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
.configure(admin::config)
|
||||
.configure(crate::auth::session::config)
|
||||
.configure(crate::auth::flows::config)
|
||||
.configure(crate::auth::pats::config)
|
||||
.configure(moderation::config)
|
||||
.configure(notifications::config)
|
||||
//.configure(pats::config)
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::ApiError;
|
||||
use crate::auth::check_is_moderator_from_headers;
|
||||
use crate::database;
|
||||
use crate::models::projects::ProjectStatus;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use actix_web::{get, web, HttpRequest, HttpResponse};
|
||||
use serde::Deserialize;
|
||||
use sqlx::PgPool;
|
||||
@@ -27,7 +27,7 @@ pub async fn get_projects(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
count: web::Query<ResultCount>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
check_is_moderator_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ use crate::auth::get_user_from_headers;
|
||||
use crate::database;
|
||||
use crate::models::ids::NotificationId;
|
||||
use crate::models::notifications::Notification;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{delete, get, patch, web, HttpRequest, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -32,9 +33,17 @@ pub async fn notifications_get(
|
||||
web::Query(ids): web::Query<NotificationIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
use database::models::notification_item::Notification as DBNotification;
|
||||
use database::models::NotificationId as DBNotificationId;
|
||||
@@ -64,9 +73,17 @@ pub async fn notification_get(
|
||||
info: web::Path<(NotificationId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let id = info.into_inner().0;
|
||||
|
||||
@@ -90,9 +107,17 @@ pub async fn notification_read(
|
||||
info: web::Path<(NotificationId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let id = info.into_inner().0;
|
||||
|
||||
@@ -125,9 +150,17 @@ pub async fn notification_delete(
|
||||
info: web::Path<(NotificationId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let id = info.into_inner().0;
|
||||
|
||||
@@ -160,9 +193,17 @@ pub async fn notifications_read(
|
||||
web::Query(ids): web::Query<NotificationIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let notification_ids = serde_json::from_str::<Vec<NotificationId>>(&ids.ids)?
|
||||
.into_iter()
|
||||
@@ -197,9 +238,17 @@ pub async fn notifications_delete(
|
||||
web::Query(ids): web::Query<NotificationIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let notification_ids = serde_json::from_str::<Vec<NotificationId>>(&ids.ids)?
|
||||
.into_iter()
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
// /*!
|
||||
// Current edition of Ory kratos does not support PAT access of data, so this module is how we allow for PAT authentication.
|
||||
//
|
||||
//
|
||||
// Just as a summary: Don't implement this flow in your application!
|
||||
// */
|
||||
//
|
||||
// use crate::database;
|
||||
// use crate::database::models::generate_pat_id;
|
||||
// use crate::models::ids::base62_impl::{parse_base62, to_base62};
|
||||
//
|
||||
// use crate::auth::get_user_from_headers;
|
||||
// use crate::auth::{generate_pat, PersonalAccessToken};
|
||||
// use crate::models::users::UserId;
|
||||
// use crate::routes::ApiError;
|
||||
//
|
||||
// use actix_web::web::{self, Data, Query};
|
||||
// use actix_web::{delete, get, patch, post, HttpRequest, HttpResponse};
|
||||
// use chrono::{Duration, Utc};
|
||||
//
|
||||
// use crate::queue::session::SessionQueue;
|
||||
// use serde::Deserialize;
|
||||
// use sqlx::postgres::PgPool;
|
||||
//
|
||||
// pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
// cfg.service(get_pats);
|
||||
// cfg.service(create_pat);
|
||||
// cfg.service(edit_pat);
|
||||
// cfg.service(delete_pat);
|
||||
// }
|
||||
//
|
||||
// #[derive(Deserialize)]
|
||||
// pub struct CreatePersonalAccessToken {
|
||||
// pub scope: i64, // todo: should be a vec of enum
|
||||
// pub name: Option<String>,
|
||||
// pub expire_in_days: i64, // resets expiry to expire_in_days days from now
|
||||
// }
|
||||
//
|
||||
// #[derive(Deserialize)]
|
||||
// pub struct ModifyPersonalAccessToken {
|
||||
// #[serde(default, with = "::serde_with::rust::double_option")]
|
||||
// pub name: Option<Option<String>>,
|
||||
// pub expire_in_days: Option<i64>, // resets expiry to expire_in_days days from now
|
||||
// }
|
||||
//
|
||||
// // GET /pat
|
||||
// // Get all personal access tokens for the given user. Minos/Kratos cookie must be attached for it to work.
|
||||
// // Does not return the actual access token, only the ID + metadata.
|
||||
// #[get("pat")]
|
||||
// pub async fn get_pats(
|
||||
// req: HttpRequest,
|
||||
// pool: Data<PgPool>,
|
||||
// redis: Data<deadpool_redis::Pool>,
|
||||
// session_queue: web::Data<SessionQueue>,
|
||||
// ) -> Result<HttpResponse, ApiError> {
|
||||
// let user: crate::models::users::User =
|
||||
// get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
// let db_user_id: database::models::UserId = database::models::UserId::from(user.id);
|
||||
//
|
||||
// let pats = sqlx::query!(
|
||||
// "
|
||||
// SELECT id, name, user_id, scope, expires_at
|
||||
// FROM pats
|
||||
// WHERE user_id = $1
|
||||
// ",
|
||||
// db_user_id.0
|
||||
// )
|
||||
// .fetch_all(&**pool)
|
||||
// .await?;
|
||||
//
|
||||
// let pats = pats
|
||||
// .into_iter()
|
||||
// .map(|pat| PersonalAccessToken {
|
||||
// id: to_base62(pat.id as u64),
|
||||
// scope: pat.scope,
|
||||
// name: pat.name,
|
||||
// expires_at: pat.expires_at,
|
||||
// access_token: None,
|
||||
// user_id: UserId(pat.user_id as u64),
|
||||
// })
|
||||
// .collect::<Vec<_>>();
|
||||
//
|
||||
// Ok(HttpResponse::Ok().json(pats))
|
||||
// }
|
||||
//
|
||||
// // POST /pat
|
||||
// // Create a new personal access token for the given user. Minos/Kratos cookie must be attached for it to work.
|
||||
// // All PAT tokens are base62 encoded, and are prefixed with "modrinth_pat_"
|
||||
// #[post("pat")]
|
||||
// pub async fn create_pat(
|
||||
// req: HttpRequest,
|
||||
// Query(info): Query<CreatePersonalAccessToken>, // callback url
|
||||
// pool: Data<PgPool>,
|
||||
// redis: web::Data<deadpool_redis::Pool>,
|
||||
// session_queue: web::Data<SessionQueue>,
|
||||
// ) -> Result<HttpResponse, ApiError> {
|
||||
// let user: crate::models::users::User =
|
||||
// get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
// let db_user_id: database::models::UserId = database::models::UserId::from(user.id);
|
||||
//
|
||||
// let mut transaction: sqlx::Transaction<sqlx::Postgres> = pool.begin().await?;
|
||||
//
|
||||
// let pat = generate_pat_id(&mut transaction).await?;
|
||||
// let access_token = generate_pat(&mut transaction).await?;
|
||||
// let expiry = Utc::now().naive_utc() + Duration::days(info.expire_in_days);
|
||||
// if info.expire_in_days <= 0 {
|
||||
// return Err(ApiError::InvalidInput(
|
||||
// "'expire_in_days' must be greater than 0".to_string(),
|
||||
// ));
|
||||
// }
|
||||
//
|
||||
// sqlx::query!(
|
||||
// "
|
||||
// INSERT INTO pats (id, name, access_token, user_id, scope, expires_at)
|
||||
// VALUES ($1, $2, $3, $4, $5, $6)
|
||||
// ",
|
||||
// pat.0,
|
||||
// info.name,
|
||||
// access_token,
|
||||
// db_user_id.0,
|
||||
// info.scope,
|
||||
// expiry
|
||||
// )
|
||||
// .execute(&mut *transaction)
|
||||
// .await?;
|
||||
//
|
||||
// transaction.commit().await?;
|
||||
//
|
||||
// Ok(HttpResponse::Ok().json(PersonalAccessToken {
|
||||
// id: to_base62(pat.0 as u64),
|
||||
// access_token: Some(access_token),
|
||||
// name: info.name,
|
||||
// scope: info.scope,
|
||||
// user_id: user.id,
|
||||
// expires_at: expiry,
|
||||
// }))
|
||||
// }
|
||||
//
|
||||
// // PATCH /pat/(id)
|
||||
// // Edit an access token of id "id" for the given user.
|
||||
// // 'None' will mean not edited. Minos/Kratos cookie or PAT must be attached for it to work.
|
||||
// #[patch("pat/{id}")]
|
||||
// pub async fn edit_pat(
|
||||
// req: HttpRequest,
|
||||
// id: web::Path<String>,
|
||||
// Query(info): Query<ModifyPersonalAccessToken>, // callback url
|
||||
// pool: Data<PgPool>,
|
||||
// redis: web::Data<deadpool_redis::Pool>,
|
||||
// session_queue: web::Data<SessionQueue>,
|
||||
// ) -> Result<HttpResponse, ApiError> {
|
||||
// let user: crate::models::users::User =
|
||||
// get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
// let pat_id = database::models::PatId(parse_base62(&id)? as i64);
|
||||
// let db_user_id: database::models::UserId = database::models::UserId::from(user.id);
|
||||
//
|
||||
// if let Some(expire_in_days) = info.expire_in_days {
|
||||
// if expire_in_days <= 0 {
|
||||
// return Err(ApiError::InvalidInput(
|
||||
// "'expire_in_days' must be greater than 0".to_string(),
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Get the singular PAT and user combination (failing immediately if it doesn't exist)
|
||||
// let mut transaction = pool.begin().await?;
|
||||
// let row = sqlx::query!(
|
||||
// "
|
||||
// SELECT id, name, scope, user_id, expires_at FROM pats
|
||||
// WHERE id = $1 AND user_id = $2
|
||||
// ",
|
||||
// pat_id.0,
|
||||
// db_user_id.0 // included for safety
|
||||
// )
|
||||
// .fetch_one(&**pool)
|
||||
// .await?;
|
||||
//
|
||||
// let pat = PersonalAccessToken {
|
||||
// id: to_base62(row.id as u64),
|
||||
// access_token: None,
|
||||
// user_id: UserId::from(db_user_id),
|
||||
// name: info.name.unwrap_or(row.name),
|
||||
// scope: row.scope,
|
||||
// expires_at: info
|
||||
// .expire_in_days
|
||||
// .map(|d| Utc::now().naive_utc() + Duration::days(d))
|
||||
// .unwrap_or(row.expires_at),
|
||||
// };
|
||||
//
|
||||
// sqlx::query!(
|
||||
// "
|
||||
// UPDATE pats SET
|
||||
// name = $1,
|
||||
// expires_at = $2
|
||||
// WHERE id = $3
|
||||
// ",
|
||||
// pat.name,
|
||||
// pat.expires_at,
|
||||
// parse_base62(&pat.id)? as i64
|
||||
// )
|
||||
// .execute(&mut *transaction)
|
||||
// .await?;
|
||||
// transaction.commit().await?;
|
||||
//
|
||||
// Ok(HttpResponse::Ok().json(pat))
|
||||
// }
|
||||
//
|
||||
// // DELETE /pat
|
||||
// // Delete a personal access token for the given user. Minos/Kratos cookie must be attached for it to work.
|
||||
// #[delete("pat/{id}")]
|
||||
// pub async fn delete_pat(
|
||||
// req: HttpRequest,
|
||||
// id: web::Path<String>,
|
||||
// pool: Data<PgPool>,
|
||||
// redis: web::Data<deadpool_redis::Pool>,
|
||||
// session_queue: web::Data<SessionQueue>,
|
||||
// ) -> Result<HttpResponse, ApiError> {
|
||||
// let user: crate::models::users::User =
|
||||
// get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
// let pat_id = database::models::PatId(parse_base62(&id)? as i64);
|
||||
// let db_user_id: database::models::UserId = database::models::UserId::from(user.id);
|
||||
//
|
||||
// // Get the singular PAT and user combination (failing immediately if it doesn't exist)
|
||||
// // This is to prevent users from deleting other users' PATs
|
||||
// let pat_id = sqlx::query!(
|
||||
// "
|
||||
// SELECT id FROM pats
|
||||
// WHERE id = $1 AND user_id = $2
|
||||
// ",
|
||||
// pat_id.0,
|
||||
// db_user_id.0
|
||||
// )
|
||||
// .fetch_one(&**pool)
|
||||
// .await?
|
||||
// .id;
|
||||
//
|
||||
// let mut transaction = pool.begin().await?;
|
||||
// sqlx::query!(
|
||||
// "
|
||||
// DELETE FROM pats
|
||||
// WHERE id = $1
|
||||
// ",
|
||||
// pat_id,
|
||||
// )
|
||||
// .execute(&mut *transaction)
|
||||
// .await?;
|
||||
// transaction.commit().await?;
|
||||
//
|
||||
// Ok(HttpResponse::Ok().finish())
|
||||
// }
|
||||
@@ -4,13 +4,14 @@ use crate::database::models;
|
||||
use crate::database::models::thread_item::ThreadBuilder;
|
||||
use crate::file_hosting::{FileHost, FileHostingError};
|
||||
use crate::models::error::ApiError;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::{
|
||||
DonationLink, License, MonetizationStatus, ProjectId, ProjectStatus, SideType, VersionId,
|
||||
VersionStatus,
|
||||
};
|
||||
use crate::models::threads::ThreadType;
|
||||
use crate::models::users::UserId;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::search::indexing::IndexingError;
|
||||
use crate::util::routes::read_from_field;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
@@ -273,7 +274,7 @@ pub async fn project_create(
|
||||
client: Data<PgPool>,
|
||||
redis: Data<deadpool_redis::Pool>,
|
||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
session_queue: Data<SessionQueue>,
|
||||
session_queue: Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let mut transaction = client.begin().await?;
|
||||
let mut uploaded_files = Vec::new();
|
||||
@@ -343,13 +344,21 @@ async fn project_create_inner(
|
||||
uploaded_files: &mut Vec<UploadedFile>,
|
||||
pool: &PgPool,
|
||||
redis: &deadpool_redis::Pool,
|
||||
session_queue: &SessionQueue,
|
||||
session_queue: &AuthQueue,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
// The base URL for files uploaded to backblaze
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
|
||||
// The currently logged in user
|
||||
let current_user = get_user_from_headers(&req, pool, redis, session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
pool,
|
||||
redis,
|
||||
session_queue,
|
||||
Some(&[Scopes::PROJECT_CREATE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let project_id: ProjectId = models::generate_project_id(transaction).await?.into();
|
||||
|
||||
@@ -726,14 +735,7 @@ async fn project_create_inner(
|
||||
}
|
||||
}
|
||||
|
||||
let thread_id = ThreadBuilder {
|
||||
type_: ThreadType::Project,
|
||||
members: vec![],
|
||||
}
|
||||
.insert(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let project_builder = models::project_item::ProjectBuilder {
|
||||
let project_builder_actual = models::project_item::ProjectBuilder {
|
||||
project_id: project_id.into(),
|
||||
project_type_id,
|
||||
team_id,
|
||||
@@ -769,12 +771,23 @@ async fn project_create_inner(
|
||||
})
|
||||
.collect(),
|
||||
color: icon_data.and_then(|x| x.1),
|
||||
thread_id,
|
||||
monetization_status: MonetizationStatus::Monetized,
|
||||
};
|
||||
let project_builder = project_builder_actual.clone();
|
||||
|
||||
let now = Utc::now();
|
||||
|
||||
let id = project_builder_actual.insert(&mut *transaction).await?;
|
||||
|
||||
let thread_id = ThreadBuilder {
|
||||
type_: ThreadType::Project,
|
||||
members: vec![],
|
||||
project_id: Some(id),
|
||||
report_id: None,
|
||||
}
|
||||
.insert(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let response = crate::models::projects::Project {
|
||||
id: project_id,
|
||||
slug: project_builder.slug.clone(),
|
||||
@@ -817,12 +830,10 @@ async fn project_create_inner(
|
||||
donation_urls: project_create_data.donation_urls.clone(),
|
||||
gallery: gallery_urls,
|
||||
color: project_builder.color,
|
||||
thread_id: Some(project_builder.thread_id.into()),
|
||||
monetization_status: project_builder.monetization_status,
|
||||
thread_id: thread_id.into(),
|
||||
monetization_status: MonetizationStatus::Monetized,
|
||||
};
|
||||
|
||||
let _project_id = project_builder.insert(&mut *transaction).await?;
|
||||
|
||||
if status == ProjectStatus::Processing {
|
||||
if let Ok(webhook_url) = dotenvy::var("MODERATION_DISCORD_WEBHOOK") {
|
||||
crate::util::webhook::send_discord_webhook(response.id, pool, webhook_url, None)
|
||||
|
||||
@@ -6,12 +6,13 @@ use crate::file_hosting::FileHost;
|
||||
use crate::models;
|
||||
use crate::models::ids::base62_impl::parse_base62;
|
||||
use crate::models::notifications::NotificationBody;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::{
|
||||
DonationLink, MonetizationStatus, Project, ProjectId, ProjectStatus, SearchRequest, SideType,
|
||||
};
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::models::threads::MessageBody;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use crate::search::{search_for_project, SearchConfig, SearchError};
|
||||
use crate::util::routes::read_from_payload;
|
||||
@@ -116,14 +117,21 @@ pub async fn projects_get(
|
||||
web::Query(ids): web::Query<ProjectIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let ids = serde_json::from_str::<Vec<&str>>(&ids.ids)?;
|
||||
let projects_data = database::models::Project::get_many(&ids, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let projects = filter_authorized_projects(projects_data, &user_option, &pool).await?;
|
||||
|
||||
@@ -136,15 +144,22 @@ pub async fn project_get(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_data = database::models::Project::get(&string, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if let Some(data) = project_data {
|
||||
if is_authorized(&data.inner, &user_option, &pool).await? {
|
||||
@@ -186,15 +201,22 @@ pub async fn dependency_list(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get(&string, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if let Some(project) = result {
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
@@ -357,9 +379,17 @@ pub async fn project_edit(
|
||||
config: web::Data<SearchConfig>,
|
||||
new_project: web::Json<EditProject>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
new_project
|
||||
.validate()
|
||||
@@ -546,18 +576,16 @@ pub async fn project_edit(
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(thread) = project_item.inner.thread_id {
|
||||
ThreadMessageBuilder {
|
||||
author_id: Some(user.id.into()),
|
||||
body: MessageBody::StatusChange {
|
||||
new_status: *status,
|
||||
old_status: project_item.inner.status,
|
||||
},
|
||||
thread_id: thread,
|
||||
}
|
||||
ThreadMessageBuilder {
|
||||
author_id: Some(user.id.into()),
|
||||
body: MessageBody::StatusChange {
|
||||
new_status: *status,
|
||||
old_status: project_item.inner.status,
|
||||
},
|
||||
thread_id: project_item.thread_id,
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -1181,9 +1209,17 @@ pub async fn projects_edit(
|
||||
pool: web::Data<PgPool>,
|
||||
bulk_edit_project: web::Json<BulkEditProject>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
bulk_edit_project
|
||||
.validate()
|
||||
@@ -1511,10 +1547,18 @@ pub async fn project_schedule(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
scheduling_data: web::Json<SchedulingData>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
if scheduling_data.time < Utc::now() {
|
||||
return Err(ApiError::InvalidInput(
|
||||
@@ -1591,11 +1635,19 @@ pub async fn project_icon_edit(
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
mut payload: web::Payload,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(&ext.ext) {
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item = database::models::Project::get(&string, &**pool, &redis)
|
||||
@@ -1687,9 +1739,17 @@ pub async fn delete_project_icon(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item = database::models::Project::get(&string, &**pool, &redis)
|
||||
@@ -1773,14 +1833,22 @@ pub async fn add_gallery_item(
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
mut payload: web::Payload,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(&ext.ext) {
|
||||
item.validate()
|
||||
.map_err(|err| ApiError::Validation(validation_errors_to_string(err, None)))?;
|
||||
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item = database::models::Project::get(&string, &**pool, &redis)
|
||||
@@ -1915,9 +1983,17 @@ pub async fn edit_gallery_item(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
item.validate()
|
||||
@@ -2061,9 +2137,17 @@ pub async fn delete_gallery_item(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project_item = database::models::Project::get(&string, &**pool, &redis)
|
||||
@@ -2148,9 +2232,17 @@ pub async fn project_delete(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
config: web::Data<SearchConfig>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_DELETE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let project = database::models::Project::get(&string, &**pool, &redis)
|
||||
@@ -2203,9 +2295,17 @@ pub async fn project_follow(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get(&string, &**pool, &redis)
|
||||
@@ -2274,9 +2374,17 @@ pub async fn project_unfollow(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get(&string, &**pool, &redis)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::auth::{check_is_moderator_from_headers, get_user_from_headers};
|
||||
use crate::database::models::thread_item::{ThreadBuilder, ThreadMessageBuilder};
|
||||
use crate::models::ids::{base62_impl::parse_base62, ProjectId, UserId, VersionId};
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::reports::{ItemType, Report};
|
||||
use crate::models::threads::{MessageBody, ThreadType};
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
|
||||
use chrono::Utc;
|
||||
@@ -35,11 +36,19 @@ pub async fn report_create(
|
||||
pool: web::Data<PgPool>,
|
||||
mut body: web::Payload,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::REPORT_CREATE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let mut bytes = web::BytesMut::new();
|
||||
while let Some(item) = body.next().await {
|
||||
@@ -59,13 +68,6 @@ pub async fn report_create(
|
||||
ApiError::InvalidInput(format!("Invalid report type: {}", new_report.report_type))
|
||||
})?;
|
||||
|
||||
let thread_id = ThreadBuilder {
|
||||
type_: ThreadType::Report,
|
||||
members: vec![],
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
let mut report = crate::database::models::report_item::Report {
|
||||
id,
|
||||
report_type_id: report_type,
|
||||
@@ -76,7 +78,6 @@ pub async fn report_create(
|
||||
reporter: current_user.id.into(),
|
||||
created: Utc::now(),
|
||||
closed: false,
|
||||
thread_id,
|
||||
};
|
||||
|
||||
match new_report.item_type {
|
||||
@@ -146,6 +147,15 @@ pub async fn report_create(
|
||||
}
|
||||
|
||||
report.insert(&mut transaction).await?;
|
||||
let thread_id = ThreadBuilder {
|
||||
type_: ThreadType::Report,
|
||||
members: vec![],
|
||||
project_id: None,
|
||||
report_id: Some(report.id),
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(Report {
|
||||
@@ -157,7 +167,7 @@ pub async fn report_create(
|
||||
body: new_report.body.clone(),
|
||||
created: Utc::now(),
|
||||
closed: false,
|
||||
thread_id: Some(report.thread_id.into()),
|
||||
thread_id: thread_id.into(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -182,9 +192,17 @@ pub async fn reports(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
count: web::Query<ReportsRequestOptions>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::REPORT_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
@@ -248,7 +266,7 @@ pub async fn reports_get(
|
||||
web::Query(ids): web::Query<ReportIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let report_ids: Vec<crate::database::models::ids::ReportId> =
|
||||
serde_json::from_str::<Vec<crate::models::ids::ReportId>>(&ids.ids)?
|
||||
@@ -259,7 +277,15 @@ pub async fn reports_get(
|
||||
let reports_data =
|
||||
crate::database::models::report_item::Report::get_many(&report_ids, &**pool).await?;
|
||||
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::REPORT_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let all_reports = reports_data
|
||||
.into_iter()
|
||||
@@ -276,9 +302,17 @@ pub async fn report_get(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
info: web::Path<(crate::models::reports::ReportId,)>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::REPORT_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id = info.into_inner().0.into();
|
||||
|
||||
let report = crate::database::models::report_item::Report::get(id, &**pool).await?;
|
||||
@@ -308,10 +342,18 @@ pub async fn report_edit(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
info: web::Path<(crate::models::reports::ReportId,)>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
edit_report: web::Json<EditReport>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::REPORT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id = info.into_inner().0.into();
|
||||
|
||||
let report = crate::database::models::report_item::Report::get(id, &**pool).await?;
|
||||
@@ -344,19 +386,17 @@ pub async fn report_edit(
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(thread) = report.thread_id {
|
||||
ThreadMessageBuilder {
|
||||
author_id: Some(user.id.into()),
|
||||
body: if !edit_closed && report.closed {
|
||||
MessageBody::ThreadReopen
|
||||
} else {
|
||||
MessageBody::ThreadClosure
|
||||
},
|
||||
thread_id: thread,
|
||||
}
|
||||
ThreadMessageBuilder {
|
||||
author_id: Some(user.id.into()),
|
||||
body: if !edit_closed && report.closed {
|
||||
MessageBody::ThreadReopen
|
||||
} else {
|
||||
MessageBody::ThreadClosure
|
||||
},
|
||||
thread_id: report.thread_id,
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
@@ -385,7 +425,7 @@ pub async fn report_delete(
|
||||
pool: web::Data<PgPool>,
|
||||
info: web::Path<(crate::models::reports::ReportId,)>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
check_is_moderator_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ pub struct CategoryData {
|
||||
header: String,
|
||||
}
|
||||
|
||||
// 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> {
|
||||
let results = Category::list(&**pool)
|
||||
|
||||
@@ -3,9 +3,10 @@ use crate::database::models::notification_item::NotificationBuilder;
|
||||
use crate::database::models::TeamMember;
|
||||
use crate::models::ids::ProjectId;
|
||||
use crate::models::notifications::NotificationBody;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::teams::{Permissions, TeamId};
|
||||
use crate::models::users::UserId;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
|
||||
use rust_decimal::Decimal;
|
||||
@@ -32,15 +33,22 @@ pub async fn team_members_get_project(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
let project_data = crate::database::models::Project::get(&string, &**pool, &redis).await?;
|
||||
|
||||
if let Some(project) = project_data {
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if !is_authorized(&project.inner, ¤t_user, &pool).await? {
|
||||
return Ok(HttpResponse::NotFound().body(""));
|
||||
@@ -93,7 +101,7 @@ pub async fn team_members_get(
|
||||
info: web::Path<(TeamId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
let members_data = TeamMember::get_from_team_full(id.into(), &**pool, &redis).await?;
|
||||
@@ -104,9 +112,16 @@ pub async fn team_members_get(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
let user_id = current_user.as_ref().map(|x| x.id.into());
|
||||
|
||||
let logged_in = current_user
|
||||
@@ -148,7 +163,7 @@ pub async fn teams_get(
|
||||
web::Query(ids): web::Query<TeamIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
use itertools::Itertools;
|
||||
|
||||
@@ -165,9 +180,16 @@ pub async fn teams_get(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let teams_groups = teams_data.into_iter().group_by(|data| data.team_id.0);
|
||||
|
||||
@@ -206,10 +228,18 @@ pub async fn join_team(
|
||||
info: web::Path<(TeamId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let team_id = info.into_inner().0.into();
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let member =
|
||||
TeamMember::get_from_user_id_pending(team_id, current_user.id.into(), &**pool).await?;
|
||||
@@ -275,13 +305,21 @@ pub async fn add_team_member(
|
||||
pool: web::Data<PgPool>,
|
||||
new_member: web::Json<NewTeamMember>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let team_id = info.into_inner().0.into();
|
||||
|
||||
let mut transaction = pool.begin().await?;
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let member = TeamMember::get_from_user_id(team_id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
@@ -390,13 +428,21 @@ pub async fn edit_team_member(
|
||||
pool: web::Data<PgPool>,
|
||||
edit_member: web::Json<EditTeamMember>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let ids = info.into_inner();
|
||||
let id = ids.0.into();
|
||||
let user_id = ids.1.into();
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let member = TeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
@@ -481,11 +527,19 @@ pub async fn transfer_ownership(
|
||||
pool: web::Data<PgPool>,
|
||||
new_owner: web::Json<TransferOwnership>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
if !current_user.role.is_admin() {
|
||||
let member = TeamMember::get_from_user_id(id.into(), current_user.id.into(), &**pool)
|
||||
@@ -554,13 +608,21 @@ pub async fn remove_team_member(
|
||||
info: web::Path<(TeamId, UserId)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let ids = info.into_inner();
|
||||
let id = ids.0.into();
|
||||
let user_id = ids.1.into();
|
||||
|
||||
let current_user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let current_user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let member = TeamMember::get_from_user_id(id, current_user.id.into(), &**pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
|
||||
@@ -4,10 +4,11 @@ use crate::database::models::notification_item::NotificationBuilder;
|
||||
use crate::database::models::thread_item::ThreadMessageBuilder;
|
||||
use crate::models::ids::ThreadMessageId;
|
||||
use crate::models::notifications::NotificationBody;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::ProjectStatus;
|
||||
use crate::models::threads::{MessageBody, Thread, ThreadId, ThreadType};
|
||||
use crate::models::users::User;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use actix_web::{delete, get, post, web, HttpRequest, HttpResponse};
|
||||
use futures::TryStreamExt;
|
||||
@@ -38,28 +39,34 @@ pub async fn is_authorized_thread(
|
||||
let user_id: database::models::UserId = user.id.into();
|
||||
Ok(match thread.type_ {
|
||||
ThreadType::Report => {
|
||||
let report_exists = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM reports WHERE thread_id = $1 AND reporter = $2)",
|
||||
thread.id as database::models::ids::ThreadId,
|
||||
user_id as database::models::ids::UserId,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.exists;
|
||||
if let Some(report_id) = thread.report_id {
|
||||
let report_exists = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM reports WHERE id = $1 AND reporter = $2)",
|
||||
report_id as database::models::ids::ReportId,
|
||||
user_id as database::models::ids::UserId,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.exists;
|
||||
|
||||
report_exists.unwrap_or(false)
|
||||
report_exists.unwrap_or(false)
|
||||
} else { false }
|
||||
}
|
||||
ThreadType::Project => {
|
||||
let project_exists = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM mods m INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.user_id = $2 WHERE thread_id = $1)",
|
||||
thread.id as database::models::ids::ThreadId,
|
||||
user_id as database::models::ids::UserId,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.exists;
|
||||
if let Some(project_id) = thread.project_id {
|
||||
let project_exists = sqlx::query!(
|
||||
"SELECT EXISTS(SELECT 1 FROM mods m INNER JOIN team_members tm ON tm.team_id = m.team_id AND tm.user_id = $2 WHERE m.id = $1)",
|
||||
project_id as database::models::ids::ProjectId,
|
||||
user_id as database::models::ids::UserId,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.exists;
|
||||
|
||||
project_exists.unwrap_or(false)
|
||||
project_exists.unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
ThreadType::DirectMessage => thread.members.contains(&user_id),
|
||||
})
|
||||
@@ -90,15 +97,15 @@ pub async fn filter_authorized_threads(
|
||||
let project_thread_ids = check_threads
|
||||
.iter()
|
||||
.filter(|x| x.type_ == ThreadType::Project)
|
||||
.map(|x| x.id.0)
|
||||
.flat_map(|x| x.project_id.map(|x| x.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !project_thread_ids.is_empty() {
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT m.thread_id FROM mods m
|
||||
SELECT m.id FROM mods m
|
||||
INNER JOIN team_members tm ON tm.team_id = m.team_id AND user_id = $2
|
||||
WHERE m.thread_id = ANY($1)
|
||||
WHERE m.id = ANY($1)
|
||||
",
|
||||
&*project_thread_ids,
|
||||
user_id as database::models::ids::UserId,
|
||||
@@ -107,7 +114,7 @@ pub async fn filter_authorized_threads(
|
||||
.try_for_each(|e| {
|
||||
if let Some(row) = e.right() {
|
||||
check_threads.retain(|x| {
|
||||
let bool = Some(x.id.0) == row.thread_id;
|
||||
let bool = x.project_id.map(|x| x.0) == Some(row.id);
|
||||
|
||||
if bool {
|
||||
return_threads.push(x.clone());
|
||||
@@ -125,14 +132,14 @@ pub async fn filter_authorized_threads(
|
||||
let report_thread_ids = check_threads
|
||||
.iter()
|
||||
.filter(|x| x.type_ == ThreadType::Report)
|
||||
.map(|x| x.id.0)
|
||||
.flat_map(|x| x.report_id.map(|x| x.0))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !report_thread_ids.is_empty() {
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT thread_id FROM reports
|
||||
WHERE thread_id = ANY($1) AND reporter = $2
|
||||
SELECT id FROM reports
|
||||
WHERE id = ANY($1) AND reporter = $2
|
||||
",
|
||||
&*report_thread_ids,
|
||||
user_id as database::models::ids::UserId,
|
||||
@@ -141,7 +148,7 @@ pub async fn filter_authorized_threads(
|
||||
.try_for_each(|e| {
|
||||
if let Some(row) = e.right() {
|
||||
check_threads.retain(|x| {
|
||||
let bool = Some(x.id.0) == row.thread_id;
|
||||
let bool = x.report_id.map(|x| x.0) == Some(row.id);
|
||||
|
||||
if bool {
|
||||
return_threads.push(x.clone());
|
||||
@@ -212,13 +219,21 @@ pub async fn thread_get(
|
||||
info: web::Path<(ThreadId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0.into();
|
||||
|
||||
let thread_data = database::models::Thread::get(string, &**pool).await?;
|
||||
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::THREAD_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
if let Some(mut data) = thread_data {
|
||||
if is_authorized_thread(&data, &user, &pool).await? {
|
||||
@@ -255,9 +270,17 @@ pub async fn threads_get(
|
||||
web::Query(ids): web::Query<ThreadIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::THREAD_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let thread_ids: Vec<database::models::ids::ThreadId> =
|
||||
serde_json::from_str::<Vec<ThreadId>>(&ids.ids)?
|
||||
@@ -284,9 +307,17 @@ pub async fn thread_send_message(
|
||||
pool: web::Data<PgPool>,
|
||||
new_message: web::Json<NewThreadMessage>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::THREAD_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let string: database::models::ThreadId = info.into_inner().0.into();
|
||||
|
||||
@@ -347,50 +378,45 @@ pub async fn thread_send_message(
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
|
||||
let mod_notif = if thread.type_ == ThreadType::Project {
|
||||
let record = sqlx::query!(
|
||||
"SELECT m.id, m.status, m.team_id FROM mods m WHERE thread_id = $1",
|
||||
thread.id as database::models::ids::ThreadId,
|
||||
let mod_notif = if let Some(project_id) = thread.project_id {
|
||||
let project = database::models::Project::get_id(
|
||||
project_id,
|
||||
&**pool,
|
||||
&redis,
|
||||
)
|
||||
.fetch_one(&**pool)
|
||||
.await?;
|
||||
|
||||
let status = ProjectStatus::from_str(&record.status);
|
||||
|
||||
if status != ProjectStatus::Processing && user.role.is_mod() {
|
||||
let members = database::models::TeamMember::get_from_team_full(
|
||||
database::models::TeamId(record.team_id),
|
||||
&**pool,
|
||||
&redis,
|
||||
)
|
||||
.await?;
|
||||
|
||||
NotificationBuilder {
|
||||
body: NotificationBody::ModeratorMessage {
|
||||
thread_id: thread.id.into(),
|
||||
message_id: id.into(),
|
||||
project_id: Some(database::models::ProjectId(record.id).into()),
|
||||
report_id: None,
|
||||
},
|
||||
if let Some(project) = project {
|
||||
if project.inner.status != ProjectStatus::Processing && user.role.is_mod() {
|
||||
let members = database::models::TeamMember::get_from_team_full(
|
||||
project.inner.team_id,
|
||||
&**pool,
|
||||
&redis,
|
||||
)
|
||||
.await?;
|
||||
|
||||
NotificationBuilder {
|
||||
body: NotificationBody::ModeratorMessage {
|
||||
thread_id: thread.id.into(),
|
||||
message_id: id.into(),
|
||||
project_id: Some(project.inner.id.into()),
|
||||
report_id: None,
|
||||
},
|
||||
}
|
||||
.insert_many(
|
||||
members.into_iter().map(|x| x.user_id).collect(),
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
.insert_many(
|
||||
members.into_iter().map(|x| x.user_id).collect(),
|
||||
&mut transaction,
|
||||
)
|
||||
.await?;
|
||||
|
||||
project.inner.status == ProjectStatus::Processing && !user.role.is_mod()
|
||||
} else {
|
||||
!user.role.is_mod()
|
||||
}
|
||||
|
||||
status == ProjectStatus::Processing && !user.role.is_mod()
|
||||
} else if thread.type_ == ThreadType::Report {
|
||||
let record = sqlx::query!(
|
||||
"SELECT r.id FROM reports r WHERE thread_id = $1",
|
||||
thread.id as database::models::ids::ThreadId,
|
||||
)
|
||||
.fetch_one(&**pool)
|
||||
.await?;
|
||||
|
||||
} else if let Some(report_id) = thread.report_id {
|
||||
let report = database::models::report_item::Report::get(
|
||||
database::models::ReportId(record.id),
|
||||
report_id,
|
||||
&**pool,
|
||||
)
|
||||
.await?;
|
||||
@@ -446,7 +472,7 @@ pub async fn moderation_inbox(
|
||||
req: HttpRequest,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = check_is_moderator_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
|
||||
@@ -474,7 +500,7 @@ pub async fn thread_read(
|
||||
info: web::Path<(ThreadId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
check_is_moderator_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
|
||||
@@ -503,9 +529,17 @@ pub async fn message_delete(
|
||||
info: web::Path<(ThreadMessageId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::THREAD_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let result = database::models::ThreadMessage::get(info.into_inner().0.into(), &**pool).await?;
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::auth::{get_user_from_headers, AuthenticationError};
|
||||
use crate::database::models::User;
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models::notifications::Notification;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::Project;
|
||||
use crate::models::users::{Badges, RecipientType, RecipientWallet, Role, UserId};
|
||||
use crate::queue::payouts::{PayoutAmount, PayoutItem, PayoutsQueue};
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::ApiError;
|
||||
use crate::util::routes::read_from_payload;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
@@ -27,7 +28,6 @@ use validator::Validate;
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(user_auth_get);
|
||||
cfg.service(user_data_get);
|
||||
cfg.service(users_get);
|
||||
|
||||
cfg.service(
|
||||
@@ -49,51 +49,36 @@ pub async fn user_auth_get(
|
||||
req: HttpRequest,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
Ok(
|
||||
HttpResponse::Ok()
|
||||
.json(get_user_from_headers(&req, &**pool, &redis, &session_queue).await?),
|
||||
let (scopes, mut user) = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_READ]),
|
||||
)
|
||||
}
|
||||
.await?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UserData {
|
||||
pub notifs_count: u64,
|
||||
pub followed_projects: Vec<crate::models::ids::ProjectId>,
|
||||
}
|
||||
|
||||
#[get("user_data")]
|
||||
pub async fn user_data_get(
|
||||
req: HttpRequest,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
|
||||
let data = sqlx::query!(
|
||||
"
|
||||
SELECT COUNT(DISTINCT n.id) notifs_count, ARRAY_AGG(mf.mod_id) followed_projects FROM notifications n
|
||||
LEFT OUTER JOIN mod_follows mf ON mf.follower_id = $1
|
||||
WHERE user_id = $1 AND read = FALSE
|
||||
",
|
||||
user.id.0 as i64
|
||||
).fetch_optional(&**pool).await?;
|
||||
|
||||
if let Some(data) = data {
|
||||
Ok(HttpResponse::Ok().json(UserData {
|
||||
notifs_count: data.notifs_count.map(|x| x as u64).unwrap_or(0),
|
||||
followed_projects: data
|
||||
.followed_projects
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|x| crate::models::ids::ProjectId(x as u64))
|
||||
.collect(),
|
||||
}))
|
||||
} else {
|
||||
Ok(HttpResponse::NoContent().body(""))
|
||||
if !scopes.contains(Scopes::USER_READ_EMAIL) {
|
||||
user.email = None;
|
||||
}
|
||||
|
||||
if !scopes.contains(Scopes::PAYOUTS_READ) {
|
||||
user.payout_data = None;
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok().json(
|
||||
get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_READ]),
|
||||
)
|
||||
.await?
|
||||
.1,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -138,11 +123,18 @@ pub async fn projects_list(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
@@ -218,9 +210,16 @@ pub async fn user_edit(
|
||||
new_user: web::Json<EditUser>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let (scopes, user) = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_WRITE]),
|
||||
)
|
||||
.await?;
|
||||
|
||||
new_user
|
||||
.validate()
|
||||
@@ -343,6 +342,12 @@ pub async fn user_edit(
|
||||
));
|
||||
}
|
||||
|
||||
if !scopes.contains(Scopes::PAYOUTS_WRITE) {
|
||||
return Err(ApiError::Authentication(
|
||||
AuthenticationError::InvalidCredentials,
|
||||
));
|
||||
}
|
||||
|
||||
if !match payout_data.payout_wallet_type {
|
||||
RecipientType::Email => {
|
||||
validator::validate_email(&payout_data.payout_address)
|
||||
@@ -401,6 +406,12 @@ pub async fn user_edit(
|
||||
}
|
||||
|
||||
if let Some((old_password, new_password)) = &new_user.password {
|
||||
if !scopes.contains(Scopes::USER_AUTH_WRITE) {
|
||||
return Err(ApiError::Authentication(
|
||||
AuthenticationError::InvalidCredentials,
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(pass) = actual_user.password {
|
||||
let old_password = old_password.as_ref().ok_or_else(|| {
|
||||
ApiError::CustomAuthentication(
|
||||
@@ -500,11 +511,19 @@ pub async fn user_icon_edit(
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
mut payload: web::Payload,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
if let Some(content_type) = crate::util::ext::get_image_content_type(&ext.ext) {
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
if let Some(actual_user) = id_option {
|
||||
@@ -579,9 +598,17 @@ pub async fn user_delete(
|
||||
pool: web::Data<PgPool>,
|
||||
removal_type: web::Query<RemovalType>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_DELETE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
@@ -619,9 +646,17 @@ pub async fn user_follows(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::USER_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
@@ -667,9 +702,17 @@ pub async fn user_notifications(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::NOTIFICATION_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
@@ -707,9 +750,17 @@ pub async fn user_payouts(
|
||||
info: web::Path<(String,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PAYOUTS_READ]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
@@ -784,11 +835,19 @@ pub async fn user_payouts_request(
|
||||
data: web::Json<PayoutData>,
|
||||
payouts_queue: web::Data<Mutex<PayoutsQueue>>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let mut payouts_queue = payouts_queue.lock().await;
|
||||
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PAYOUTS_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
|
||||
|
||||
if let Some(id) = id_option.map(|x| x.id) {
|
||||
|
||||
@@ -8,12 +8,13 @@ use crate::database::models::version_item::{
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models::notifications::NotificationBody;
|
||||
use crate::models::pack::PackFileHash;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::{
|
||||
Dependency, DependencyType, FileType, GameVersion, Loader, ProjectId, Version, VersionFile,
|
||||
VersionId, VersionStatus, VersionType,
|
||||
};
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::util::routes::read_from_field;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
use crate::validate::{validate_file, ValidationResult};
|
||||
@@ -85,7 +86,7 @@ pub async fn version_create(
|
||||
client: Data<PgPool>,
|
||||
redis: Data<deadpool_redis::Pool>,
|
||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
session_queue: Data<SessionQueue>,
|
||||
session_queue: Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let mut transaction = client.begin().await?;
|
||||
let mut uploaded_files = Vec::new();
|
||||
@@ -127,7 +128,7 @@ async fn version_create_inner(
|
||||
file_host: &dyn FileHost,
|
||||
uploaded_files: &mut Vec<UploadedFile>,
|
||||
pool: &PgPool,
|
||||
session_queue: &SessionQueue,
|
||||
session_queue: &AuthQueue,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
|
||||
@@ -137,7 +138,15 @@ async fn version_create_inner(
|
||||
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, pool, redis, session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
pool,
|
||||
redis,
|
||||
session_queue,
|
||||
Some(&[Scopes::VERSION_CREATE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let mut error = None;
|
||||
while let Some(item) = payload.next().await {
|
||||
@@ -441,7 +450,7 @@ pub async fn upload_file_to_version(
|
||||
client: Data<PgPool>,
|
||||
redis: Data<deadpool_redis::Pool>,
|
||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let mut transaction = client.begin().await?;
|
||||
let mut uploaded_files = Vec::new();
|
||||
@@ -487,14 +496,22 @@ async fn upload_file_to_version_inner(
|
||||
file_host: &dyn FileHost,
|
||||
uploaded_files: &mut Vec<UploadedFile>,
|
||||
version_id: models::VersionId,
|
||||
session_queue: &SessionQueue,
|
||||
session_queue: &AuthQueue,
|
||||
) -> Result<HttpResponse, CreateError> {
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
|
||||
let mut initial_file_data: Option<InitialFileData> = None;
|
||||
let mut file_builders: Vec<VersionFileBuilder> = Vec::new();
|
||||
|
||||
let user = get_user_from_headers(&req, &**client, &redis, session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**client,
|
||||
&redis,
|
||||
session_queue,
|
||||
Some(&[Scopes::VERSION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let result = models::Version::get(version_id, &**client, &redis).await?;
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ use crate::auth::{
|
||||
is_authorized_version,
|
||||
};
|
||||
use crate::models::ids::VersionId;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::VersionType;
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::{database, models};
|
||||
use actix_web::{delete, get, post, web, HttpRequest, HttpResponse};
|
||||
use itertools::Itertools;
|
||||
@@ -49,11 +50,18 @@ pub async fn get_version_from_hash(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
hash_query: web::Query<HashQuery>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let hash = info.into_inner().0.to_lowercase();
|
||||
let file = database::models::Version::get_file_from_hash(
|
||||
@@ -95,11 +103,18 @@ pub async fn download_version(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
hash_query: web::Query<HashQuery>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let hash = info.into_inner().0.to_lowercase();
|
||||
let file = database::models::Version::get_file_from_hash(
|
||||
@@ -138,9 +153,17 @@ pub async fn delete_file(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
hash_query: web::Query<HashQuery>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
let hash = info.into_inner().0.to_lowercase();
|
||||
|
||||
@@ -234,11 +257,18 @@ pub async fn get_update_from_hash(
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
hash_query: web::Query<HashQuery>,
|
||||
update_data: web::Json<UpdateData>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
let hash = info.into_inner().0.to_lowercase();
|
||||
|
||||
if let Some(file) = database::models::Version::get_file_from_hash(
|
||||
@@ -304,11 +334,18 @@ pub async fn get_versions_from_hashes(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_data: web::Json<FileHashes>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let files = database::models::Version::get_files_from_hash(
|
||||
file_data.algorithm.clone(),
|
||||
@@ -345,11 +382,18 @@ pub async fn get_projects_from_hashes(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
file_data: web::Json<FileHashes>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let files = database::models::Version::get_files_from_hash(
|
||||
file_data.algorithm.clone(),
|
||||
@@ -396,11 +440,18 @@ pub async fn update_files(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
update_data: web::Json<ManyUpdateData>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let files = database::models::Version::get_files_from_hash(
|
||||
update_data.algorithm.clone(),
|
||||
|
||||
@@ -4,9 +4,10 @@ use crate::auth::{
|
||||
};
|
||||
use crate::database;
|
||||
use crate::models;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::projects::{Dependency, FileType, VersionStatus, VersionType};
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::queue::session::SessionQueue;
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -45,15 +46,22 @@ pub async fn version_list(
|
||||
web::Query(filters): web::Query<VersionListFilters>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let string = info.into_inner().0;
|
||||
|
||||
let result = database::models::Project::get(&string, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ, Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if let Some(project) = result {
|
||||
if !is_authorized(&project.inner, &user_option, &pool).await? {
|
||||
@@ -154,15 +162,22 @@ pub async fn version_project_get(
|
||||
info: web::Path<(String, String)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner();
|
||||
let version_data =
|
||||
database::models::Version::get_full_from_id_slug(&id.0, &id.1, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::PROJECT_READ, Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if let Some(data) = version_data {
|
||||
if is_authorized_version(&data.inner, &user_option, &pool).await? {
|
||||
@@ -184,7 +199,7 @@ pub async fn versions_get(
|
||||
web::Query(ids): web::Query<VersionIds>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let version_ids = serde_json::from_str::<Vec<models::ids::VersionId>>(&ids.ids)?
|
||||
.into_iter()
|
||||
@@ -192,9 +207,16 @@ pub async fn versions_get(
|
||||
.collect::<Vec<database::models::VersionId>>();
|
||||
let versions_data = database::models::Version::get_many(&version_ids, &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
let versions = filter_authorized_versions(versions_data, &user_option, &pool).await?;
|
||||
|
||||
@@ -207,14 +229,21 @@ pub async fn version_get(
|
||||
info: web::Path<(models::ids::VersionId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let id = info.into_inner().0;
|
||||
let version_data = database::models::Version::get(id.into(), &**pool, &redis).await?;
|
||||
|
||||
let user_option = get_user_from_headers(&req, &**pool, &redis, &session_queue)
|
||||
.await
|
||||
.ok();
|
||||
let user_option = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_READ]),
|
||||
)
|
||||
.await
|
||||
.map(|x| x.1)
|
||||
.ok();
|
||||
|
||||
if let Some(data) = version_data {
|
||||
if is_authorized_version(&data.inner, &user_option, &pool).await? {
|
||||
@@ -268,9 +297,17 @@ pub async fn version_edit(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
new_version: web::Json<EditVersion>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
new_version
|
||||
.validate()
|
||||
@@ -645,9 +682,17 @@ pub async fn version_schedule(
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
scheduling_data: web::Json<SchedulingData>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_WRITE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
|
||||
if scheduling_data.time < Utc::now() {
|
||||
return Err(ApiError::InvalidInput(
|
||||
@@ -711,9 +756,17 @@ pub async fn version_delete(
|
||||
info: web::Path<(models::ids::VersionId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
redis: web::Data<deadpool_redis::Pool>,
|
||||
session_queue: web::Data<SessionQueue>,
|
||||
session_queue: web::Data<AuthQueue>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(&req, &**pool, &redis, &session_queue).await?;
|
||||
let user = get_user_from_headers(
|
||||
&req,
|
||||
&**pool,
|
||||
&redis,
|
||||
&session_queue,
|
||||
Some(&[Scopes::VERSION_DELETE]),
|
||||
)
|
||||
.await?
|
||||
.1;
|
||||
let id = info.into_inner().0;
|
||||
|
||||
let version = database::models::Version::get(id.into(), &**pool, &redis)
|
||||
|
||||
Reference in New Issue
Block a user