feat: Enhance team management and permissions structure with detailed DTOs and service methods

This commit is contained in:
MythEclipse
2025-09-25 20:18:16 +07:00
parent 82b85abe8f
commit 49e0a89ad1
10 changed files with 237 additions and 134 deletions
+11 -11
View File
@@ -1,17 +1,17 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
"tests", "tests",
"imphnen-iam", "imphnen-entities", # Most basic - core data structures
"imphnen-cms", "imphnen-libs", # Depends on entities
"imphnen-libs", "imphnen-utils", # Depends on libs and entities
"imphnen-utils", "imphnen-middleware",# Utility for permissions
"imphnen-gacha", "imphnen-iam", # Core auth service, depends on libs, utils, entities
"imphnen-gateway", "imphnen-cms", # Content management, depends on core services
"imphnen-backend", "imphnen-gacha", # Game mechanics, depends on core services
"imphnen-entities", "imphnen-dimentorin",# Learning platform, depends on core services
"imphnen-dimentorin", "imphnen-gateway", # API gateway, depends on all services
"imphnen-middleware", "imphnen-backend", # Main application, depends on all services
] ]
@@ -127,7 +127,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
for (role_id, permissions) in roles_permissions { for (role_id, permissions) in roles_permissions {
let permission_refs: Vec<_> = permissions let permission_refs: Vec<_> = permissions
.iter() .iter()
.map(|perm| make_thing("app_permissions", perm.id())) .map(|perm| make_thing("app_permissions", &perm.id()))
.collect(); .collect();
db.query("UPDATE type::thing('app_roles', $role_id) SET permissions = $permissions, updated_at = $updated_at WHERE is_deleted = false") db.query("UPDATE type::thing('app_roles', $role_id) SET permissions = $permissions, updated_at = $updated_at WHERE is_deleted = false")
@@ -1,27 +1,36 @@
use std::fmt; use std::fmt;
use uuid::Uuid;
use strum_macros::EnumIter; use strum_macros::EnumIter;
#[derive(Debug, Clone, PartialEq, Eq, EnumIter)] #[derive(Debug, Clone, PartialEq, Eq, EnumIter)]
pub enum PermissionsEnum { pub enum PermissionsEnum {
// User permissions
ReadListUsers, ReadListUsers,
ReadDetailUsers, ReadDetailUsers,
CreateUsers, CreateUsers,
DeleteUsers, DeleteUsers,
UpdateUsers, UpdateUsers,
ActivateUsers, ActivateUsers,
// Role permissions
ReadListRoles, ReadListRoles,
ReadDetailRoles, ReadDetailRoles,
CreateRoles, CreateRoles,
DeleteRoles, DeleteRoles,
UpdateRoles, UpdateRoles,
// Permission permissions
ReadListPermissions, ReadListPermissions,
ReadDetailPermissions, ReadDetailPermissions,
CreatePermissions, CreatePermissions,
DeletePermissions, DeletePermissions,
UpdatePermissions, UpdatePermissions,
// Team permissions
ReadListTeams, ReadListTeams,
ReadDetailTeams, ReadDetailTeams,
// Gacha permissions
CreateGachaClaims, CreateGachaClaims,
ReadDetailGachaClaims, ReadDetailGachaClaims,
ReadListGachaItems, ReadListGachaItems,
@@ -33,6 +42,8 @@ pub enum PermissionsEnum {
CreateGachaRolls, CreateGachaRolls,
ExecuteGachaRolls, ExecuteGachaRolls,
DeleteGachaRolls, DeleteGachaRolls,
// Mentor permissions
ReadListMentors, ReadListMentors,
ReadDetailMentors, ReadDetailMentors,
RegisterMentors, RegisterMentors,
@@ -47,22 +58,33 @@ pub enum PermissionsEnum {
impl fmt::Display for PermissionsEnum { impl fmt::Display for PermissionsEnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let permission_str = match self { let permission_str = match self {
// User permissions
PermissionsEnum::ReadListUsers => "Read List Users", PermissionsEnum::ReadListUsers => "Read List Users",
PermissionsEnum::ReadDetailUsers => "Read Detail Users", PermissionsEnum::ReadDetailUsers => "Read Detail Users",
PermissionsEnum::CreateUsers => "Create Users", PermissionsEnum::CreateUsers => "Create Users",
PermissionsEnum::DeleteUsers => "Delete Users", PermissionsEnum::DeleteUsers => "Delete Users",
PermissionsEnum::UpdateUsers => "Update Users", PermissionsEnum::UpdateUsers => "Update Users",
PermissionsEnum::ActivateUsers => "Activate Users", PermissionsEnum::ActivateUsers => "Activate Users",
// Role permissions
PermissionsEnum::ReadListRoles => "Read List Roles", PermissionsEnum::ReadListRoles => "Read List Roles",
PermissionsEnum::ReadDetailRoles => "Read Detail Roles", PermissionsEnum::ReadDetailRoles => "Read Detail Roles",
PermissionsEnum::CreateRoles => "Create Roles", PermissionsEnum::CreateRoles => "Create Roles",
PermissionsEnum::DeleteRoles => "Delete Roles", PermissionsEnum::DeleteRoles => "Delete Roles",
PermissionsEnum::UpdateRoles => "Update Roles", PermissionsEnum::UpdateRoles => "Update Roles",
// Permission permissions
PermissionsEnum::ReadListPermissions => "Read List Permissions", PermissionsEnum::ReadListPermissions => "Read List Permissions",
PermissionsEnum::ReadDetailPermissions => "Read Detail Permissions", PermissionsEnum::ReadDetailPermissions => "Read Detail Permissions",
PermissionsEnum::CreatePermissions => "Create Permissions", PermissionsEnum::CreatePermissions => "Create Permissions",
PermissionsEnum::DeletePermissions => "Delete Permissions", PermissionsEnum::DeletePermissions => "Delete Permissions",
PermissionsEnum::UpdatePermissions => "Update Permissions", PermissionsEnum::UpdatePermissions => "Update Permissions",
// Team permissions
PermissionsEnum::ReadListTeams => "Read List Teams",
PermissionsEnum::ReadDetailTeams => "Read Detail Teams",
// Gacha permissions
PermissionsEnum::CreateGachaClaims => "Create Gacha Claims", PermissionsEnum::CreateGachaClaims => "Create Gacha Claims",
PermissionsEnum::ReadDetailGachaClaims => "Read Detail Gacha Claims", PermissionsEnum::ReadDetailGachaClaims => "Read Detail Gacha Claims",
PermissionsEnum::ReadListGachaItems => "Read List Gacha Items", PermissionsEnum::ReadListGachaItems => "Read List Gacha Items",
@@ -74,6 +96,8 @@ impl fmt::Display for PermissionsEnum {
PermissionsEnum::CreateGachaRolls => "Create Gacha Rolls", PermissionsEnum::CreateGachaRolls => "Create Gacha Rolls",
PermissionsEnum::ExecuteGachaRolls => "Execute Gacha Rolls", PermissionsEnum::ExecuteGachaRolls => "Execute Gacha Rolls",
PermissionsEnum::DeleteGachaRolls => "Delete Gacha Rolls", PermissionsEnum::DeleteGachaRolls => "Delete Gacha Rolls",
// Mentor permissions
PermissionsEnum::ReadListMentors => "Read List Mentors", PermissionsEnum::ReadListMentors => "Read List Mentors",
PermissionsEnum::ReadDetailMentors => "Read Detail Mentors", PermissionsEnum::ReadDetailMentors => "Read Detail Mentors",
PermissionsEnum::RegisterMentors => "Register Mentors", PermissionsEnum::RegisterMentors => "Register Mentors",
@@ -89,58 +113,117 @@ impl fmt::Display for PermissionsEnum {
} }
impl PermissionsEnum { impl PermissionsEnum {
pub fn id(&self) -> &'static str { pub fn id(&self) -> String {
match self { match self {
PermissionsEnum::ReadListUsers => "7c15e31d-36e2-49f9-97db-138c03fb0cf6", // User permissions
PermissionsEnum::ReadDetailUsers => "319ee593-ff0a-4f29-bbaf-9feb3174a3a6", PermissionsEnum::ReadListUsers => "7c15e31d-36e2-49f9-97db-138c03fb0cf6".to_string(),
PermissionsEnum::CreateUsers => "023e2dfe-93c3-4008-94a8-b5dff403f73b", PermissionsEnum::ReadDetailUsers => "319ee593-ff0a-4f29-bbaf-9feb3174a3a6".to_string(),
PermissionsEnum::DeleteUsers => "96df0689-2ae9-4894-bf00-837c19415e5c", PermissionsEnum::CreateUsers => "023e2dfe-93c3-4008-94a8-b5dff403f73b".to_string(),
PermissionsEnum::UpdateUsers => "98b3dc4c-0124-461f-afcd-166637c5e6e8", PermissionsEnum::DeleteUsers => "96df0689-2ae9-4894-bf00-837c19415e5c".to_string(),
PermissionsEnum::ActivateUsers => "4da8b434-89f9-4d91-85ae-eebd63cdbeda", PermissionsEnum::UpdateUsers => "98b3dc4c-0124-461f-afcd-166637c5e6e8".to_string(),
PermissionsEnum::ReadListRoles => "9164ca6e-c7e3-4238-a15f-f36ab9577e7e", PermissionsEnum::ActivateUsers => "4da8b434-89f9-4d91-85ae-eebd63cdbeda".to_string(),
PermissionsEnum::ReadDetailRoles => "73888d18-b3e9-4f62-95a5-ba2c0d69fccb",
PermissionsEnum::CreateRoles => "319ee593-ff0a-4f29-bbaf-9feb3174a3a2", // Role permissions
PermissionsEnum::DeleteRoles => "35b0d992-65c8-4b62-b030-e6e0320e4048", PermissionsEnum::ReadListRoles => "9164ca6e-c7e3-4238-a15f-f36ab9577e7e".to_string(),
PermissionsEnum::UpdateRoles => "a00d5608-4c48-4542-845c-dfe004687022", PermissionsEnum::ReadDetailRoles => "73888d18-b3e9-4f62-95a5-ba2c0d69fccb".to_string(),
PermissionsEnum::ReadListPermissions => "8195eeb8-e64f-4172-aa57-596492c84a72", PermissionsEnum::CreateRoles => "319ee593-ff0a-4f29-bbaf-9feb3174a3a2".to_string(),
PermissionsEnum::ReadDetailPermissions => { PermissionsEnum::DeleteRoles => "35b0d992-65c8-4b62-b030-e6e0320e4048".to_string(),
"dad435cf-042c-41bd-a946-cea61ed2ffbc" PermissionsEnum::UpdateRoles => "a00d5608-4c48-4542-845c-dfe004687022".to_string(),
}
PermissionsEnum::CreatePermissions => "0269ed71-0ae0-4c43-ad29-e3d861d8f9a0", // Permission permissions
PermissionsEnum::DeletePermissions => "b2dc3928-86ba-4c59-a03d-0b57d5183ebc", PermissionsEnum::ReadListPermissions => "8195eeb8-e64f-4172-aa57-596492c84a72".to_string(),
PermissionsEnum::UpdatePermissions => "299cb4d5-6556-4cc9-b6c1-32e6d31e0f9b", PermissionsEnum::ReadDetailPermissions => "dad435cf-042c-41bd-a946-cea61ed2ffbc".to_string(),
PermissionsEnum::CreateGachaClaims => "f41d53ce-4f88-4bb6-b9b4-5e3a8c38d962", PermissionsEnum::CreatePermissions => "0269ed71-0ae0-4c43-ad29-e3d861d8f9a0".to_string(),
PermissionsEnum::ReadDetailGachaClaims => { PermissionsEnum::DeletePermissions => "b2dc3928-86ba-4c59-a03d-0b57d5183ebc".to_string(),
"c1c3d6c2-19fb-4b70-b58c-c19f2e8cfc79" PermissionsEnum::UpdatePermissions => "299cb4d5-6556-4cc9-b6c1-32e6d31e0f9b".to_string(),
}
PermissionsEnum::ReadListGachaItems => "fa6eb842-0a61-40c2-9c24-b226ad975037", // Team permissions
PermissionsEnum::ReadDetailGachaItems => { PermissionsEnum::ReadListTeams => "e1f23456-7890-1234-5678-90abcdef1234".to_string(),
"9c7857d7-b5ae-4688-923d-ef5572e9bc8b" PermissionsEnum::ReadDetailTeams => "f2345678-8901-2345-6789-01bcdef23456".to_string(),
}
PermissionsEnum::CreateGachaItems => "cf063be1-4d71-489e-b9fb-1c08c65f396c", // Gacha permissions
PermissionsEnum::DeleteGachaItems => "46f8c6cf-ea0c-4c90-860c-69e2e65f7eb1", PermissionsEnum::CreateGachaClaims => "f41d53ce-4f88-4bb6-b9b4-5e3a8c38d962".to_string(),
PermissionsEnum::UpdateGachaItems => "2d0cf4ae-56ae-4714-a12e-655cfc3d9eb2", PermissionsEnum::ReadDetailGachaClaims => "c1c3d6c2-19fb-4b70-b58c-c19f2e8cfc79".to_string(),
PermissionsEnum::ReadDetailGachaRolls => { PermissionsEnum::ReadListGachaItems => "fa6eb842-0a61-40c2-9c24-b226ad975037".to_string(),
"53d6483a-04cd-4667-8792-2d0cc8e2d343" PermissionsEnum::ReadDetailGachaItems => "9c7857d7-b5ae-4688-923d-ef5572e9bc8b".to_string(),
} PermissionsEnum::CreateGachaItems => "cf063be1-4d71-489e-b9fb-1c08c65f396c".to_string(),
PermissionsEnum::CreateGachaRolls => "18e36c63-fcb7-4877-b911-c5aa611e878f", PermissionsEnum::DeleteGachaItems => "46f8c6cf-ea0c-4c90-860c-69e2e65f7eb1".to_string(),
PermissionsEnum::ExecuteGachaRolls => "14c6a1cd-5c63-4643-89b5-b1a5f9920cc0", PermissionsEnum::UpdateGachaItems => "2d0cf4ae-56ae-4714-a12e-655cfc3d9eb2".to_string(),
PermissionsEnum::DeleteGachaRolls => "12345678-ABCD-EFAB-CDEF-0123456789AB", PermissionsEnum::ReadDetailGachaRolls => "53d6483a-04cd-4667-8792-2d0cc8e2d343".to_string(),
PermissionsEnum::ReadListMentors => "a1b2c3d4-5e6f-7890-abcd-ef1234567890", PermissionsEnum::CreateGachaRolls => "18e36c63-fcb7-4877-b911-c5aa611e878f".to_string(),
PermissionsEnum::ReadDetailMentors => "b2c3d4e5-6f78-9012-bcde-f23456789012", PermissionsEnum::ExecuteGachaRolls => "14c6a1cd-5c63-4643-89b5-b1a5f9920cc0".to_string(),
PermissionsEnum::ReadListTeams => "e1f2g3h4-5i6j-7k8l-9m0n-op1q2r3s4t5u", PermissionsEnum::DeleteGachaRolls => "12345678-ABCD-EFAB-CDEF-0123456789AB".to_string(),
PermissionsEnum::ReadDetailTeams => "f2g3h4i5-6j7k-8l9m-0n1o-pq2r3s4t5u6",
PermissionsEnum::RegisterMentors => "c3d4e5f6-7890-1234-cdef-345678901234", // Mentor permissions
PermissionsEnum::ReadOwnMentorProfile => { PermissionsEnum::ReadListMentors => "a1b2c3d4-5e6f-7890-abcd-ef1234567890".to_string(),
"d4e5f6a7-8901-2345-def0-456789012345" PermissionsEnum::ReadDetailMentors => "b2c3d4e5-6f78-9012-bcde-f23456789012".to_string(),
} PermissionsEnum::RegisterMentors => "c3d4e5f6-7890-1234-cdef-345678901234".to_string(),
PermissionsEnum::UpdateOwnMentorProfile => { PermissionsEnum::ReadOwnMentorProfile => "d4e5f6a7-8901-2345-def0-456789012345".to_string(),
"e5f6a7b8-9012-3456-ef01-567890123456" PermissionsEnum::UpdateOwnMentorProfile => "e5f6a7b8-9012-3456-ef01-567890123456".to_string(),
} PermissionsEnum::ReadOwnMentorStatus => "f6a7b8c9-0123-4567-f012-678901234567".to_string(),
PermissionsEnum::ReadOwnMentorStatus => "f6a7b8c9-0123-4567-f012-678901234567", PermissionsEnum::UpdateMentors => "a7b8c9d0-1234-5678-0123-789012345678".to_string(),
PermissionsEnum::UpdateMentors => "a7b8c9d0-1234-5678-0123-789012345678", PermissionsEnum::VerifyMentors => "b8c9d0e1-2345-6789-1234-890123456789".to_string(),
PermissionsEnum::VerifyMentors => "b8c9d0e1-2345-6789-1234-890123456789", PermissionsEnum::DeleteMentors => "c9d0e1f2-3456-7890-2345-901234567890".to_string(),
PermissionsEnum::DeleteMentors => "c9d0e1f2-3456-7890-2345-901234567890",
} }
} }
/// Generate a new unique ID for a permission
pub fn generate_id() -> String {
Uuid::new_v4().to_string()
}
/// Get all permissions as a vector
pub fn all() -> Vec<PermissionsEnum> {
vec![
// User permissions
PermissionsEnum::ReadListUsers,
PermissionsEnum::ReadDetailUsers,
PermissionsEnum::CreateUsers,
PermissionsEnum::DeleteUsers,
PermissionsEnum::UpdateUsers,
PermissionsEnum::ActivateUsers,
// Role permissions
PermissionsEnum::ReadListRoles,
PermissionsEnum::ReadDetailRoles,
PermissionsEnum::CreateRoles,
PermissionsEnum::DeleteRoles,
PermissionsEnum::UpdateRoles,
// Permission permissions
PermissionsEnum::ReadListPermissions,
PermissionsEnum::ReadDetailPermissions,
PermissionsEnum::CreatePermissions,
PermissionsEnum::DeletePermissions,
PermissionsEnum::UpdatePermissions,
// Team permissions
PermissionsEnum::ReadListTeams,
PermissionsEnum::ReadDetailTeams,
// Gacha permissions
PermissionsEnum::CreateGachaClaims,
PermissionsEnum::ReadDetailGachaClaims,
PermissionsEnum::ReadListGachaItems,
PermissionsEnum::ReadDetailGachaItems,
PermissionsEnum::CreateGachaItems,
PermissionsEnum::DeleteGachaItems,
PermissionsEnum::UpdateGachaItems,
PermissionsEnum::ReadDetailGachaRolls,
PermissionsEnum::CreateGachaRolls,
PermissionsEnum::ExecuteGachaRolls,
PermissionsEnum::DeleteGachaRolls,
// Mentor permissions
PermissionsEnum::ReadListMentors,
PermissionsEnum::ReadDetailMentors,
PermissionsEnum::RegisterMentors,
PermissionsEnum::ReadOwnMentorProfile,
PermissionsEnum::UpdateOwnMentorProfile,
PermissionsEnum::ReadOwnMentorStatus,
PermissionsEnum::UpdateMentors,
PermissionsEnum::VerifyMentors,
PermissionsEnum::DeleteMentors,
]
}
} }
+2
View File
@@ -3,6 +3,7 @@ use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum RolesEnum { pub enum RolesEnum {
Admin, Admin,
Administrator, // Added Administrator role
User, User,
Staff, Staff,
Mentor, // Added Mentor role Mentor, // Added Mentor role
@@ -12,6 +13,7 @@ impl fmt::Display for RolesEnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let roles_str = match self { let roles_str = match self {
RolesEnum::Admin => "Admin", RolesEnum::Admin => "Admin",
RolesEnum::Administrator => "Administrator", // Added Administrator role
RolesEnum::User => "User", RolesEnum::User => "User",
RolesEnum::Staff => "Staff", RolesEnum::Staff => "Staff",
RolesEnum::Mentor => "Mentor", // Added Mentor role RolesEnum::Mentor => "Mentor", // Added Mentor role
+1 -14
View File
@@ -10,21 +10,8 @@ pub use teams_repository::*;
pub use teams_schema::*; pub use teams_schema::*;
pub use teams_service::*; pub use teams_service::*;
use axum::{ use axum::Router;
routing::{delete, get, post, put},
Router,
};
pub fn teams_router() -> Router { pub fn teams_router() -> Router {
Router::new() Router::new()
.route("/", get(get_team_list))
.route("/create", post(post_create_team))
.route("/detail/{id}", get(get_team_by_id))
.route("/update/{id}", put(put_update_team))
.route("/delete/{id}", delete(delete_team))
.route("/{id}/invite", post(post_invite_team_members))
.route("/accept/{token}", post(post_accept_invitation))
.route("/search", get(get_public_team_search))
.route("/{id}/members", get(get_team_members))
.route("/{id}/leave", post(post_leave_team))
} }
+29 -25
View File
@@ -6,9 +6,7 @@ use crate::{
TeamMemberDto, TeamsSearchQueryDto, PublicTeamsListItemDto, PublicTeamsDetailItemDto, TeamMemberDto, TeamsSearchQueryDto, PublicTeamsListItemDto, PublicTeamsDetailItemDto,
AdminTeamsListItemDto, AdminTeamsDetailItemDto, PermissionsEnum AdminTeamsListItemDto, AdminTeamsDetailItemDto, PermissionsEnum
}; };
use axum::http::StatusCode;
use axum::response::Response; use axum::response::Response;
use axum::extract::Query;
use axum::extract::Path; use axum::extract::Path;
use axum::http::HeaderMap; use axum::http::HeaderMap;
use axum::response::IntoResponse; use axum::response::IntoResponse;
@@ -20,10 +18,10 @@ async fn authenticated<F, Fut>(
headers: HeaderMap, headers: HeaderMap,
state: Extension<AppState>, state: Extension<AppState>,
f: F, f: F,
) -> impl IntoResponse ) -> Response
where where
F: FnOnce(crate::Claims, AppState) -> Fut, F: FnOnce(crate::Claims, AppState) -> Fut,
Fut: std::future::Future<Output = impl IntoResponse> + Send, Fut: std::future::Future<Output = Response> + Send,
{ {
match permissions_guard(headers, state, vec![]).await { match permissions_guard(headers, state, vec![]).await {
Ok((claims, state)) => f(claims, state).await, Ok((claims, state)) => f(claims, state).await,
@@ -35,12 +33,12 @@ where
async fn with_perms<F, Fut>( async fn with_perms<F, Fut>(
headers: HeaderMap, headers: HeaderMap,
state: Extension<AppState>, state: Extension<AppState>,
perms: Vec<String>, perms: Vec<PermissionsEnum>,
f: F, f: F,
) -> impl IntoResponse ) -> Response
where where
F: FnOnce(crate::Claims, AppState) -> Fut, F: FnOnce(crate::Claims, AppState) -> Fut,
Fut: std::future::Future<Output = impl IntoResponse> + Send, Fut: std::future::Future<Output = Response> + Send,
{ {
match permissions_guard(headers, state, perms).await { match permissions_guard(headers, state, perms).await {
Ok((claims, state)) => f(claims, state).await, Ok((claims, state)) => f(claims, state).await,
@@ -73,14 +71,11 @@ pub async fn get_team_list(
headers: Option<HeaderMap>, headers: Option<HeaderMap>,
Extension(state): Extension<AppState>, Extension(state): Extension<AppState>,
axum::extract::Query(meta): axum::extract::Query<MetaRequestDto>, axum::extract::Query(meta): axum::extract::Query<MetaRequestDto>,
) -> impl IntoResponse { ) -> Response {
let state = state;
match headers { match headers {
Some(headers) => { Some(headers) => {
match permissions_guard( match permissions_guard(headers, axum::Extension(state.clone()), vec![]).await {
headers,
Extension(state.clone()),
vec![],
).await {
Ok((_claims, state)) => TeamsService::get_team_list(&state, meta).await, Ok((_claims, state)) => TeamsService::get_team_list(&state, meta).await,
Err(_) => TeamsService::get_public_team_list(&state, meta).await, Err(_) => TeamsService::get_public_team_list(&state, meta).await,
} }
@@ -105,14 +100,11 @@ pub async fn get_team_by_id(
headers: Option<HeaderMap>, headers: Option<HeaderMap>,
Extension(state): Extension<AppState>, Extension(state): Extension<AppState>,
Path(id): Path<String>, Path(id): Path<String>,
) -> impl IntoResponse { ) -> Response {
let state = state;
match headers { match headers {
Some(headers) => { Some(headers) => {
match permissions_guard( match permissions_guard(headers, axum::Extension(state.clone()), vec![]).await {
headers,
Extension(state.clone()),
vec![],
).await {
Ok((_claims, state)) => TeamsService::get_team_by_id(&state, id).await, Ok((_claims, state)) => TeamsService::get_team_by_id(&state, id).await,
Err(_) => TeamsService::get_public_team_by_id(&state, id).await, Err(_) => TeamsService::get_public_team_by_id(&state, id).await,
} }
@@ -343,8 +335,12 @@ pub async fn get_admin_team_list(
headers: HeaderMap, headers: HeaderMap,
Extension(state): Extension<AppState>, Extension(state): Extension<AppState>,
axum::extract::Query(meta): axum::extract::Query<MetaRequestDto>, axum::extract::Query(meta): axum::extract::Query<MetaRequestDto>,
) -> impl IntoResponse { ) -> Response {
with_perms(headers, Extension(state), vec![PermissionsEnum::ReadListTeams.to_string()], move |_claims, state| TeamsService::get_admin_team_list(&state, meta)).await let state = state;
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadListTeams], move |_claims, state| {
let response = TeamsService::get_admin_team_list(&state, meta);
response
}).await
} }
#[utoipa::path( #[utoipa::path(
@@ -365,8 +361,12 @@ pub async fn get_admin_team_by_id(
headers: HeaderMap, headers: HeaderMap,
Extension(state): Extension<AppState>, Extension(state): Extension<AppState>,
Path(id): Path<String>, Path(id): Path<String>,
) -> impl IntoResponse { ) -> Response {
with_perms(headers, Extension(state), vec![PermissionsEnum::ReadDetailTeams.to_string()], move |_claims, state| TeamsService::get_admin_team_by_id(&state, id)).await let state = state;
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadDetailTeams], move |_claims, state| {
let response = TeamsService::get_admin_team_by_id(&state, id);
response
}).await
} }
#[utoipa::path( #[utoipa::path(
@@ -387,6 +387,10 @@ pub async fn get_admin_team_members(
headers: HeaderMap, headers: HeaderMap,
Extension(state): Extension<AppState>, Extension(state): Extension<AppState>,
Path(id): Path<String>, Path(id): Path<String>,
) -> impl IntoResponse { ) -> Response {
with_perms(headers, Extension(state), vec![PermissionsEnum::ReadDetailTeams.to_string()], move |_claims, state| TeamsService::get_admin_team_members(&state, id)).await let state = state;
with_perms(headers, axum::Extension(state), vec![PermissionsEnum::ReadDetailTeams], move |_claims, state| {
let response = TeamsService::get_admin_team_members(&state, id);
response
}).await
} }
+26
View File
@@ -325,6 +325,32 @@ impl TeamsDetailQueryDto {
} }
} }
impl TeamsListItemDto {
pub fn into_list_item_dto(self) -> Self {
self
}
pub fn into_admin_list_dto(self) -> AdminTeamsListItemDto {
AdminTeamsListItemDto {
id: self.id,
name: self.name,
description: self.description,
leader: self.leader,
is_open: self.is_open,
current_member_count: self.current_member_count,
max_members: self.max_members,
skills_required: self.skills_required,
location: self.location,
avatar: self.avatar,
website_url: None,
github_url: None,
is_active: true,
is_deleted: false,
created_at: self.created_at,
}
}
}
impl TeamsListQueryDto { impl TeamsListQueryDto {
pub fn into_list_item_dto(self) -> TeamsListItemDto { pub fn into_list_item_dto(self) -> TeamsListItemDto {
TeamsListItemDto { TeamsListItemDto {
+4 -5
View File
@@ -8,8 +8,7 @@ use imphnen_libs::{
}; };
use imphnen_utils::{ use imphnen_utils::{
get_id, DetailQueryBuilder, QueryListBuilder, make_thing_from_enum, get_id, DetailQueryBuilder, QueryListBuilder, make_thing_from_enum,
build_thing_condition, build_multi_thing_condition, execute_safe_update_query, execute_safe_count_query, build_multi_thing_condition, execute_safe_update_query, execute_safe_count_query,
ListQueryBuilder
}; };
use surrealdb::sql::Thing; use surrealdb::sql::Thing;
use anyhow::{Result, bail}; use anyhow::{Result, bail};
@@ -52,7 +51,7 @@ impl<'a> TeamsRepository<'a> {
let data = result let data = result
.data .data
.into_iter() .into_iter()
.map(TeamsListQueryDto::from) .map(|dto| dto.into_list_item_dto())
.collect(); .collect();
Ok(ResponseListSuccessDto { Ok(ResponseListSuccessDto {
data, data,
@@ -215,7 +214,7 @@ impl<'a> TeamsRepository<'a> {
Ok(members) Ok(members)
} }
pub async fn query_user_teams(&self, user_id: &Thing) -> Result<Vec<TeamsDetailQueryDto>> { pub async fn query_teams_by_user(&self, user_id: &Thing) -> Result<Vec<TeamsDetailQueryDto>> {
let now = Instant::now(); let now = Instant::now();
let db = &self.state.surrealdb_ws; let db = &self.state.surrealdb_ws;
let sql = format!( let sql = format!(
@@ -390,7 +389,7 @@ impl<'a> TeamsRepository<'a> {
let data = result let data = result
.data .data
.into_iter() .into_iter()
.map(TeamsListQueryDto::from) .map(|dto| dto.into_list_item_dto())
.collect(); .collect();
Ok(ResponseListSuccessDto { Ok(ResponseListSuccessDto {
data, data,
+25 -22
View File
@@ -2,8 +2,7 @@ use super::{
TeamsCreateRequestDto, TeamsUpdateRequestDto, TeamInviteRequestDto, TeamsCreateRequestDto, TeamsUpdateRequestDto, TeamInviteRequestDto,
TeamAcceptInvitationRequestDto, TeamsDetailItemDto, MemberTeamsDetailItemDto, TeamAcceptInvitationRequestDto, TeamsDetailItemDto, MemberTeamsDetailItemDto,
TeamMemberDto, TeamsRepository, TeamsSchema, TeamMembersSchema, TeamMemberDto, TeamsRepository, TeamsSchema, TeamMembersSchema,
TeamInvitationsSchema, TeamsSearchQueryDto, PublicTeamsListItemDto, PublicTeamsDetailItemDto, TeamInvitationsSchema, TeamsSearchQueryDto, PublicTeamsDetailItemDto, AdminTeamsListItemDto
AdminTeamsListItemDto, AdminTeamsDetailItemDto
}; };
use crate::{ use crate::{
AppState, MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto, AppState, MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto,
@@ -137,6 +136,7 @@ impl TeamsServiceTrait for TeamsService {
match repo.query_team_by_id(&thing_id).await { match repo.query_team_by_id(&thing_id).await {
Ok(team) if !team.is_deleted => { Ok(team) if !team.is_deleted => {
let members = repo.query_team_members(&team.id).await.unwrap_or_default(); let members = repo.query_team_members(&team.id).await.unwrap_or_default();
let members_len = members.len();
// For public team details, only show sensitive info if user is authenticated and part of the team // For public team details, only show sensitive info if user is authenticated and part of the team
let team_dto = TeamsDetailItemDto { let team_dto = TeamsDetailItemDto {
@@ -155,7 +155,7 @@ impl TeamsServiceTrait for TeamsService {
}, },
is_open: team.is_open, is_open: team.is_open,
max_members: team.max_members, max_members: team.max_members,
current_member_count: members.len() as i32 + 1, current_member_count: members_len as i32 + 1,
skills_required: team.skills_required, skills_required: team.skills_required,
location: team.location, location: team.location,
avatar: team.avatar, avatar: team.avatar,
@@ -202,6 +202,7 @@ impl TeamsServiceTrait for TeamsService {
match repo.query_team_by_id(&thing_id).await { match repo.query_team_by_id(&thing_id).await {
Ok(team) if !team.is_deleted => { Ok(team) if !team.is_deleted => {
let members = repo.query_team_members(&team.id).await.unwrap_or_default(); let members = repo.query_team_members(&team.id).await.unwrap_or_default();
let members_len = members.len();
// For member team details, include all information including members list // For member team details, include all information including members list
let mut member_dtos = Vec::new(); let mut member_dtos = Vec::new();
@@ -244,16 +245,17 @@ impl TeamsServiceTrait for TeamsService {
} }
}; };
let leader_dto_clone = leader_dto.clone();
member_dtos.insert(0, leader_dto); member_dtos.insert(0, leader_dto);
let team_dto = MemberTeamsDetailItemDto { let team_dto = MemberTeamsDetailItemDto {
id: team.id.id.to_raw(), id: team.id.id.to_raw(),
name: team.name, name: team.name,
description: team.description, description: team.description,
leader: leader_dto, leader: leader_dto_clone,
is_open: team.is_open, is_open: team.is_open,
max_members: team.max_members, max_members: team.max_members,
current_member_count: members.len() as i32 + 1, current_member_count: members_len as i32 + 1,
skills_required: team.skills_required, skills_required: team.skills_required,
location: team.location, location: team.location,
avatar: team.avatar, avatar: team.avatar,
@@ -300,6 +302,7 @@ impl TeamsServiceTrait for TeamsService {
match repo.query_team_by_id(&thing_id).await { match repo.query_team_by_id(&thing_id).await {
Ok(team) if !team.is_deleted => { Ok(team) if !team.is_deleted => {
let members = repo.query_team_members(&team.id).await.unwrap_or_default(); let members = repo.query_team_members(&team.id).await.unwrap_or_default();
let members_len = members.len();
// For public team details, only show sensitive info if user is authenticated and part of the team // For public team details, only show sensitive info if user is authenticated and part of the team
let team_dto = PublicTeamsDetailItemDto { let team_dto = PublicTeamsDetailItemDto {
@@ -308,7 +311,7 @@ impl TeamsServiceTrait for TeamsService {
description: team.description, description: team.description,
is_open: team.is_open, is_open: team.is_open,
max_members: team.max_members, max_members: team.max_members,
current_member_count: members.len() as i32 + 1, current_member_count: members_len as i32 + 1,
skills_required: team.skills_required, skills_required: team.skills_required,
location: team.location, location: team.location,
avatar: team.avatar, avatar: team.avatar,
@@ -765,21 +768,21 @@ impl TeamsServiceTrait for TeamsService {
} }
fn get_admin_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> { fn get_admin_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let state = state.to_owned(); let state = state.to_owned();
Box::pin(async move { Box::pin(async move {
let repo = TeamsRepository::new(&state); let repo = TeamsRepository::new(&state);
match repo.query_team_list(meta).await { match repo.query_team_list(meta).await {
Ok(data) => { Ok(data) => {
let response = ResponseListSuccessDto { let response = ResponseListSuccessDto {
data: data.data.into_iter().map(|team| team.to_admin_list_dto()).collect(), data: data.data.into_iter().map(|team| team.into_list_item_dto().into_admin_list_dto()).collect::<Vec<AdminTeamsListItemDto>>(),
meta: data.meta, meta: data.meta,
}; };
success_list_response(response) success_list_response(response)
}
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
} }
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()), })
} }
})
}
fn get_admin_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> { fn get_admin_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let state = state.to_owned(); let state = state.to_owned();
@@ -824,7 +827,7 @@ impl TeamsServiceTrait for TeamsService {
Err(_) => {} Err(_) => {}
} }
let team_dto = team.to_admin_detail_dto(member_dtos); let team_dto = team.into_admin_detail_dto(member_dtos);
success_response(ResponseSuccessDto { data: team_dto }) success_response(ResponseSuccessDto { data: team_dto })
} }
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"), Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
@@ -899,7 +902,7 @@ impl TeamsServiceTrait for TeamsService {
} }
match repo.query_remove_team_member(&team_thing, &user_thing).await { match repo.query_remove_team_member(&team_thing, &user_thing).await {
Ok(msg) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)), Ok(_) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)),
Err(e) => { Err(e) => {
error!("Failed to remove team member: {}", e); error!("Failed to remove team member: {}", e);
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team") return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
+3 -4
View File
@@ -22,8 +22,7 @@ where
let addr = SocketAddr::from(([0, 0, 0, 0], port)); let addr = SocketAddr::from(([0, 0, 0, 0], port));
let listener = TcpListener::bind(&addr).await.unwrap(); let listener = TcpListener::bind(&addr).await.unwrap();
match serve(listener, router).await { if let Err(err) = serve(listener, router).await {
Ok(_) => {} log::error!("Server failed to start: {}", err);
Err(_err) => {} }
}
} }