You've already forked AstralRinth
forked from didirus/AstralRinth
Route to view user's orgs (#742)
This commit is contained in:
22
.sqlx/query-1d356243ac743720af11e6a49d17148618caa3be7cf33bc0859e51b06eede6e9.json
generated
Normal file
22
.sqlx/query-1d356243ac743720af11e6a49d17148618caa3be7cf33bc0859e51b06eede6e9.json
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT o.id FROM organizations o\n INNER JOIN team_members tm ON tm.team_id = o.team_id AND tm.accepted = TRUE\n WHERE tm.user_id = $1\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "1d356243ac743720af11e6a49d17148618caa3be7cf33bc0859e51b06eede6e9"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "\n SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color\n FROM organizations o\n WHERE o.id = ANY($1) OR o.title = ANY($2)\n GROUP BY o.id;\n ",
|
"query": "\n SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color\n FROM organizations o\n WHERE o.id = ANY($1) OR LOWER(o.title) = ANY($2)\n GROUP BY o.id;\n ",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
@@ -49,5 +49,5 @@
|
|||||||
true
|
true
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "05047ef3c49f2b90f5d090f69f8e7f626843d9487d5e63a28e8efe28e27cb9ad"
|
"hash": "30307fb92fd2d8e1f03f21f8ad76f285ef8cb2bf8f40f9facafaae3f8c75d587"
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ impl Organization {
|
|||||||
"
|
"
|
||||||
SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color
|
SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color
|
||||||
FROM organizations o
|
FROM organizations o
|
||||||
WHERE o.id = ANY($1) OR o.title = ANY($2)
|
WHERE o.id = ANY($1) OR LOWER(o.title) = ANY($2)
|
||||||
GROUP BY o.id;
|
GROUP BY o.id;
|
||||||
",
|
",
|
||||||
&organization_ids_parsed,
|
&organization_ids_parsed,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::ids::{ProjectId, UserId};
|
use super::ids::{ProjectId, UserId};
|
||||||
use super::CollectionId;
|
use super::CollectionId;
|
||||||
use crate::database::models::DatabaseError;
|
use crate::database::models::{DatabaseError, OrganizationId};
|
||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
use crate::models::ids::base62_impl::{parse_base62, to_base62};
|
use crate::models::ids::base62_impl::{parse_base62, to_base62};
|
||||||
use crate::models::users::{Badges, RecipientStatus};
|
use crate::models::users::{Badges, RecipientStatus};
|
||||||
@@ -307,6 +307,31 @@ impl User {
|
|||||||
Ok(db_projects)
|
Ok(db_projects)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_organizations<'a, E>(
|
||||||
|
user_id: UserId,
|
||||||
|
exec: E,
|
||||||
|
) -> Result<Vec<OrganizationId>, sqlx::Error>
|
||||||
|
where
|
||||||
|
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||||
|
{
|
||||||
|
use futures::stream::TryStreamExt;
|
||||||
|
|
||||||
|
let orgs = sqlx::query!(
|
||||||
|
"
|
||||||
|
SELECT o.id FROM organizations o
|
||||||
|
INNER JOIN team_members tm ON tm.team_id = o.team_id AND tm.accepted = TRUE
|
||||||
|
WHERE tm.user_id = $1
|
||||||
|
",
|
||||||
|
user_id as UserId,
|
||||||
|
)
|
||||||
|
.fetch_many(exec)
|
||||||
|
.try_filter_map(|e| async { Ok(e.right().map(|m| OrganizationId(m.id))) })
|
||||||
|
.try_collect::<Vec<OrganizationId>>()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(orgs)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_collections<'a, E>(
|
pub async fn get_collections<'a, E>(
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
exec: E,
|
exec: E,
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ pub async fn organization_create(
|
|||||||
let mut transaction = pool.begin().await?;
|
let mut transaction = pool.begin().await?;
|
||||||
|
|
||||||
// Try title
|
// Try title
|
||||||
let title_organization_id_option: Option<OrganizationId> =
|
let title_organization_id_option: Option<u64> = parse_base62(&new_organization.title).ok();
|
||||||
serde_json::from_str(&format!("\"{}\"", new_organization.title)).ok();
|
|
||||||
let mut organization_strings = vec![];
|
let mut organization_strings = vec![];
|
||||||
if let Some(title_organization_id) = title_organization_id_option {
|
if let Some(title_organization_id) = title_organization_id_option {
|
||||||
organization_strings.push(title_organization_id.to_string());
|
organization_strings.push(title_organization_id.to_string());
|
||||||
@@ -93,7 +92,7 @@ pub async fn organization_create(
|
|||||||
let team = team_item::TeamBuilder {
|
let team = team_item::TeamBuilder {
|
||||||
members: vec![team_item::TeamMemberBuilder {
|
members: vec![team_item::TeamMemberBuilder {
|
||||||
user_id: current_user.id.into(),
|
user_id: current_user.id.into(),
|
||||||
role: crate::models::teams::OWNER_ROLE.to_owned(),
|
role: models::teams::OWNER_ROLE.to_owned(),
|
||||||
permissions: ProjectPermissions::all(),
|
permissions: ProjectPermissions::all(),
|
||||||
organization_permissions: Some(OrganizationPermissions::all()),
|
organization_permissions: Some(OrganizationPermissions::all()),
|
||||||
accepted: true,
|
accepted: true,
|
||||||
@@ -218,7 +217,7 @@ pub async fn organizations_get(
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let teams_data = TeamMember::get_from_team_full_many(&team_ids, &**pool, &redis).await?;
|
let teams_data = TeamMember::get_from_team_full_many(&team_ids, &**pool, &redis).await?;
|
||||||
let users = crate::database::models::User::get_many_ids(
|
let users = database::models::User::get_many_ids(
|
||||||
&teams_data.iter().map(|x| x.user_id).collect::<Vec<_>>(),
|
&teams_data.iter().map(|x| x.user_id).collect::<Vec<_>>(),
|
||||||
&**pool,
|
&**pool,
|
||||||
&redis,
|
&redis,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::database::models::{self, image_item, User};
|
|||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
use crate::file_hosting::{FileHost, FileHostingError};
|
use crate::file_hosting::{FileHost, FileHostingError};
|
||||||
use crate::models::error::ApiError;
|
use crate::models::error::ApiError;
|
||||||
|
use crate::models::ids::base62_impl::parse_base62;
|
||||||
use crate::models::ids::ImageId;
|
use crate::models::ids::ImageId;
|
||||||
use crate::models::images::{Image, ImageContext};
|
use crate::models::images::{Image, ImageContext};
|
||||||
use crate::models::pats::Scopes;
|
use crate::models::pats::Scopes;
|
||||||
@@ -417,16 +418,14 @@ async fn project_create_inner(
|
|||||||
.validate()
|
.validate()
|
||||||
.map_err(|err| CreateError::InvalidInput(validation_errors_to_string(err, None)))?;
|
.map_err(|err| CreateError::InvalidInput(validation_errors_to_string(err, None)))?;
|
||||||
|
|
||||||
let slug_project_id_option: Option<ProjectId> =
|
let slug_project_id_option: Option<u64> = parse_base62(&create_data.slug).ok();
|
||||||
serde_json::from_str(&format!("\"{}\"", create_data.slug)).ok();
|
|
||||||
|
|
||||||
if let Some(slug_project_id) = slug_project_id_option {
|
if let Some(slug_project_id) = slug_project_id_option {
|
||||||
let slug_project_id: models::ids::ProjectId = slug_project_id.into();
|
|
||||||
let results = sqlx::query!(
|
let results = sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)
|
SELECT EXISTS(SELECT 1 FROM mods WHERE id=$1)
|
||||||
",
|
",
|
||||||
slug_project_id as models::ids::ProjectId
|
slug_project_id as i64
|
||||||
)
|
)
|
||||||
.fetch_one(&mut **transaction)
|
.fetch_one(&mut **transaction)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use rust_decimal::Decimal;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
@@ -32,6 +33,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||||||
cfg.service(
|
cfg.service(
|
||||||
web::scope("user")
|
web::scope("user")
|
||||||
.service(user_get)
|
.service(user_get)
|
||||||
|
.service(orgs_list)
|
||||||
.service(projects_list)
|
.service(projects_list)
|
||||||
.service(collections_list)
|
.service(collections_list)
|
||||||
.service(user_delete)
|
.service(user_delete)
|
||||||
@@ -196,6 +198,89 @@ pub async fn collections_list(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("{user_id}/organizatons")]
|
||||||
|
pub async fn orgs_list(
|
||||||
|
req: HttpRequest,
|
||||||
|
info: web::Path<(String,)>,
|
||||||
|
pool: web::Data<PgPool>,
|
||||||
|
redis: web::Data<RedisPool>,
|
||||||
|
session_queue: web::Data<AuthQueue>,
|
||||||
|
) -> Result<HttpResponse, ApiError> {
|
||||||
|
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?;
|
||||||
|
|
||||||
|
if let Some(id) = id_option.map(|x| x.id) {
|
||||||
|
let org_data = User::get_organizations(id, &**pool).await?;
|
||||||
|
|
||||||
|
let organizations_data =
|
||||||
|
crate::database::models::organization_item::Organization::get_many_ids(
|
||||||
|
&org_data, &**pool, &redis,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let team_ids = organizations_data
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.team_id)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let teams_data = crate::database::models::TeamMember::get_from_team_full_many(
|
||||||
|
&team_ids, &**pool, &redis,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let users = User::get_many_ids(
|
||||||
|
&teams_data.iter().map(|x| x.user_id).collect::<Vec<_>>(),
|
||||||
|
&**pool,
|
||||||
|
&redis,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut organizations = vec![];
|
||||||
|
let mut team_groups = HashMap::new();
|
||||||
|
for item in teams_data {
|
||||||
|
team_groups.entry(item.team_id).or_insert(vec![]).push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for data in organizations_data {
|
||||||
|
let members_data = team_groups.remove(&data.team_id).unwrap_or(vec![]);
|
||||||
|
let logged_in = user
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|user| {
|
||||||
|
members_data
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.user_id == user.id.into() && x.accepted)
|
||||||
|
})
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
let team_members: Vec<_> = members_data
|
||||||
|
.into_iter()
|
||||||
|
.filter(|x| logged_in || x.accepted || id == x.user_id)
|
||||||
|
.flat_map(|data| {
|
||||||
|
users.iter().find(|x| x.id == data.user_id).map(|user| {
|
||||||
|
crate::models::teams::TeamMember::from(data, user.clone(), !logged_in)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let organization = crate::models::organizations::Organization::from(data, team_members);
|
||||||
|
organizations.push(organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(organizations))
|
||||||
|
} else {
|
||||||
|
Ok(HttpResponse::NotFound().body(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
|
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user