a
This commit is contained in:
@@ -5,129 +5,129 @@ use validator::Validate;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct TeamsCreateRequestDto {
|
||||
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
||||
pub name: String,
|
||||
|
||||
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_open: Option<bool>,
|
||||
|
||||
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_members: Option<i32>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
|
||||
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid avatar URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avatar: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid website URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub website_url: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid GitHub URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub github_url: Option<String>,
|
||||
|
||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||
pub member_emails: Vec<String>,
|
||||
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
||||
pub name: String,
|
||||
|
||||
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_open: Option<bool>,
|
||||
|
||||
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_members: Option<i32>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
|
||||
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid avatar URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avatar: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid website URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub website_url: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid GitHub URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub github_url: Option<String>,
|
||||
|
||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||
pub member_emails: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct TeamsUpdateRequestDto {
|
||||
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
|
||||
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_open: Option<bool>,
|
||||
|
||||
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_members: Option<i32>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
|
||||
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid avatar URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avatar: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid website URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub website_url: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid GitHub URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub github_url: Option<String>,
|
||||
#[validate(length(min = 3, max = 100, message = "Team name must be between 3 and 100 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
|
||||
#[validate(length(max = 500, message = "Description cannot exceed 500 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub is_open: Option<bool>,
|
||||
|
||||
#[validate(range(min = 2, max = 50, message = "Max members must be between 2 and 50"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_members: Option<i32>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
|
||||
#[validate(length(max = 100, message = "Location cannot exceed 100 characters"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid avatar URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avatar: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid website URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub website_url: Option<String>,
|
||||
|
||||
#[validate(url(message = "Invalid GitHub URL"))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub github_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct TeamInviteRequestDto {
|
||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||
pub member_emails: Vec<String>,
|
||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||
pub member_emails: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TeamAcceptInvitationRequestDto {
|
||||
pub token: String,
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TeamsDetailItemDto {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader: TeamMemberDto,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub current_member_count: i32,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub github_url: Option<String>,
|
||||
pub members: Option<Vec<TeamMemberDto>>,
|
||||
pub is_active: bool,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader: TeamMemberDto,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub current_member_count: i32,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub github_url: Option<String>,
|
||||
pub members: Option<Vec<TeamMemberDto>>,
|
||||
pub is_active: bool,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct MemberTeamsDetailItemDto {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader: TeamMemberDto,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub current_member_count: i32,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub github_url: Option<String>,
|
||||
pub members: Vec<TeamMemberDto>, // Always include members for authenticated users
|
||||
pub is_active: bool,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader: TeamMemberDto,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub current_member_count: i32,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub github_url: Option<String>,
|
||||
pub members: Vec<TeamMemberDto>, // Always include members for authenticated users
|
||||
pub is_active: bool,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
@@ -179,168 +179,106 @@ pub struct PublicTeamsDetailItemDto {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TeamMemberDto {
|
||||
pub id: String,
|
||||
pub user_id: String,
|
||||
pub fullname: String,
|
||||
pub email: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub role: String,
|
||||
pub skills: Option<Vec<String>>,
|
||||
pub joined_at: String,
|
||||
pub id: String,
|
||||
pub user_id: String,
|
||||
pub fullname: String,
|
||||
pub email: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub role: String,
|
||||
pub skills: Option<Vec<String>>,
|
||||
pub joined_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TeamInvitationDto {
|
||||
pub id: String,
|
||||
pub team_id: String,
|
||||
pub team_name: String,
|
||||
pub email: String,
|
||||
pub inviter_name: String,
|
||||
pub status: String,
|
||||
pub expires_at: String,
|
||||
pub invited_at: String,
|
||||
pub id: String,
|
||||
pub team_id: String,
|
||||
pub team_name: String,
|
||||
pub email: String,
|
||||
pub inviter_name: String,
|
||||
pub status: String,
|
||||
pub expires_at: String,
|
||||
pub invited_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TeamsDetailQueryDto {
|
||||
pub id: Thing,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader_id: Thing,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub github_url: Option<String>,
|
||||
pub is_active: bool,
|
||||
pub is_deleted: bool,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub id: Thing,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader_id: Thing,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub website_url: Option<String>,
|
||||
pub github_url: Option<String>,
|
||||
pub is_active: bool,
|
||||
pub is_deleted: bool,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TeamsListQueryDto {
|
||||
pub id: Thing,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader_id: Thing,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub id: Thing,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub leader_id: Thing,
|
||||
pub is_open: bool,
|
||||
pub max_members: Option<i32>,
|
||||
pub skills_required: Option<Vec<String>>,
|
||||
pub location: Option<String>,
|
||||
pub avatar: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TeamMembersQueryDto {
|
||||
pub id: Thing,
|
||||
pub team_id: Thing,
|
||||
pub user_id: Thing,
|
||||
pub role: String,
|
||||
pub joined_at: String,
|
||||
pub is_active: bool,
|
||||
pub id: Thing,
|
||||
pub team_id: Thing,
|
||||
pub user_id: Thing,
|
||||
pub role: String,
|
||||
pub joined_at: String,
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct TeamInvitationsQueryDto {
|
||||
pub id: Thing,
|
||||
pub team_id: Thing,
|
||||
pub email: String,
|
||||
pub inviter_id: Thing,
|
||||
pub invite_code: String,
|
||||
pub expires_at: String,
|
||||
pub status: String,
|
||||
pub invited_at: String,
|
||||
pub accepted_at: Option<String>,
|
||||
pub id: Thing,
|
||||
pub team_id: Thing,
|
||||
pub email: String,
|
||||
pub inviter_id: Thing,
|
||||
pub invite_code: String,
|
||||
pub expires_at: String,
|
||||
pub status: String,
|
||||
pub invited_at: String,
|
||||
pub accepted_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct TeamsSearchQueryDto {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub query: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub open: Option<bool>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skills: Option<Vec<String>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub page: Option<i64>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub per_page: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub query: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub open: Option<bool>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub skills: Option<Vec<String>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub page: Option<i64>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub per_page: Option<i64>,
|
||||
}
|
||||
|
||||
impl TeamsDetailQueryDto {
|
||||
pub fn from(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TeamsListQueryDto {
|
||||
pub fn from(self) -> TeamsListItemDto {
|
||||
TeamsListItemDto {
|
||||
id: self.id.id.to_raw(),
|
||||
name: self.name,
|
||||
description: self.description,
|
||||
leader: TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: self.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: self.created_at.clone(),
|
||||
},
|
||||
is_open: self.is_open,
|
||||
current_member_count: 1,
|
||||
max_members: self.max_members,
|
||||
skills_required: self.skills_required,
|
||||
location: self.location,
|
||||
avatar: self.avatar,
|
||||
created_at: self.created_at,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_admin_list_dto(self) -> AdminTeamsListItemDto {
|
||||
let members = vec![];
|
||||
AdminTeamsListItemDto {
|
||||
id: self.id.id.to_raw(),
|
||||
name: self.name,
|
||||
description: self.description,
|
||||
leader: TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: self.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: self.created_at.clone(),
|
||||
},
|
||||
is_open: self.is_open,
|
||||
current_member_count: members.len() as i32 + 1,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct AdminTeamsListItemDto {
|
||||
pub id: String,
|
||||
@@ -381,9 +319,39 @@ pub struct AdminTeamsDetailItemDto {
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
impl TeamsDetailQueryDto {
|
||||
pub fn into_detail_dto(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TeamsListQueryDto {
|
||||
pub fn to_admin_list_dto(self) -> AdminTeamsListItemDto {
|
||||
let members = vec![];
|
||||
pub fn into_list_item_dto(self) -> TeamsListItemDto {
|
||||
TeamsListItemDto {
|
||||
id: self.id.id.to_raw(),
|
||||
name: self.name,
|
||||
description: self.description,
|
||||
leader: TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: self.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: self.created_at.clone(),
|
||||
},
|
||||
is_open: self.is_open,
|
||||
current_member_count: 1,
|
||||
max_members: self.max_members,
|
||||
skills_required: self.skills_required,
|
||||
location: self.location,
|
||||
avatar: self.avatar,
|
||||
created_at: self.created_at,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_admin_list_dto(self) -> AdminTeamsListItemDto {
|
||||
AdminTeamsListItemDto {
|
||||
id: self.id.id.to_raw(),
|
||||
name: self.name,
|
||||
@@ -399,7 +367,7 @@ impl TeamsListQueryDto {
|
||||
joined_at: self.created_at.clone(),
|
||||
},
|
||||
is_open: self.is_open,
|
||||
current_member_count: members.len() as i32 + 1,
|
||||
current_member_count: 1, // Placeholder; adjust based on actual member count
|
||||
max_members: self.max_members,
|
||||
skills_required: self.skills_required,
|
||||
location: self.location,
|
||||
@@ -414,7 +382,7 @@ impl TeamsListQueryDto {
|
||||
}
|
||||
|
||||
impl TeamsDetailQueryDto {
|
||||
pub fn to_admin_detail_dto(self, members: Vec<TeamMemberDto>) -> AdminTeamsDetailItemDto {
|
||||
pub fn into_admin_detail_dto(self, members: Vec<TeamMemberDto>) -> AdminTeamsDetailItemDto {
|
||||
AdminTeamsDetailItemDto {
|
||||
id: self.id.id.to_raw(),
|
||||
name: self.name,
|
||||
@@ -445,4 +413,3 @@ impl TeamsDetailQueryDto {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,167 +110,167 @@ impl TeamsService {
|
||||
|
||||
impl TeamsServiceTrait for TeamsService {
|
||||
fn get_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
let repo = TeamsRepository::new(&state);
|
||||
match repo.query_team_list(meta).await {
|
||||
Ok(data) => {
|
||||
let response = ResponseListSuccessDto {
|
||||
data: data.data,
|
||||
meta: data.meta,
|
||||
};
|
||||
success_list_response(response)
|
||||
}
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
let repo = TeamsRepository::new(&state);
|
||||
match repo.query_team_list(meta).await {
|
||||
Ok(data) => {
|
||||
let response = ResponseListSuccessDto {
|
||||
data: data.data,
|
||||
meta: data.meta,
|
||||
};
|
||||
success_list_response(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
fn get_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) if !team.is_deleted => {
|
||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||
|
||||
// For public team details, only show sensitive info if user is authenticated and part of the team
|
||||
let team_dto = TeamsDetailItemDto {
|
||||
id: team.id.id.to_raw(),
|
||||
name: team.name,
|
||||
description: team.description,
|
||||
leader: TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: team.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: team.created_at.clone(),
|
||||
},
|
||||
is_open: team.is_open,
|
||||
max_members: team.max_members,
|
||||
current_member_count: members.len() as i32 + 1,
|
||||
skills_required: team.skills_required,
|
||||
location: team.location,
|
||||
avatar: team.avatar,
|
||||
website_url: team.website_url,
|
||||
github_url: team.github_url,
|
||||
members: None,
|
||||
is_active: team.is_active,
|
||||
created_at: team.created_at,
|
||||
updated_at: team.updated_at,
|
||||
};
|
||||
success_response(ResponseSuccessDto { data: team_dto })
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) if !team.is_deleted => {
|
||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||
|
||||
// For public team details, only show sensitive info if user is authenticated and part of the team
|
||||
let team_dto = TeamsDetailItemDto {
|
||||
id: team.id.id.to_raw(),
|
||||
name: team.name,
|
||||
description: team.description,
|
||||
leader: TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: team.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: team.created_at.clone(),
|
||||
},
|
||||
is_open: team.is_open,
|
||||
max_members: team.max_members,
|
||||
current_member_count: members.len() as i32 + 1,
|
||||
skills_required: team.skills_required,
|
||||
location: team.location,
|
||||
avatar: team.avatar,
|
||||
website_url: team.website_url,
|
||||
github_url: team.github_url,
|
||||
members: None,
|
||||
is_active: team.is_active,
|
||||
created_at: team.created_at,
|
||||
updated_at: team.updated_at,
|
||||
};
|
||||
success_response(ResponseSuccessDto { data: team_dto })
|
||||
}
|
||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_member_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
let repo = TeamsRepository::new(&state);
|
||||
match repo.query_team_list(meta).await {
|
||||
Ok(data) => {
|
||||
let response = ResponseListSuccessDto {
|
||||
data: data.data,
|
||||
meta: data.meta,
|
||||
};
|
||||
success_list_response(response)
|
||||
}
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
fn get_member_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
let repo = TeamsRepository::new(&state);
|
||||
match repo.query_team_list(meta).await {
|
||||
Ok(data) => {
|
||||
let response = ResponseListSuccessDto {
|
||||
data: data.data,
|
||||
meta: data.meta,
|
||||
};
|
||||
success_list_response(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_member_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) if !team.is_deleted => {
|
||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||
|
||||
// For member team details, include all information including members list
|
||||
let mut member_dtos = Vec::new();
|
||||
for member in members {
|
||||
match Self::get_user_info_with_privacy(
|
||||
&member.user_id.id.to_raw(),
|
||||
"system", // In member context, we show all user info
|
||||
true, // In member context, we show all user info
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut member_dto) => {
|
||||
member_dto.role = member.role;
|
||||
member_dto.joined_at = member.joined_at;
|
||||
member_dtos.push(member_dto);
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
// Add leader with full info
|
||||
let leader_dto = match Self::get_user_info_with_privacy(
|
||||
&team.leader_id.id.to_raw(),
|
||||
"system",
|
||||
true,
|
||||
fn get_member_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) if !team.is_deleted => {
|
||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||
|
||||
// For member team details, include all information including members list
|
||||
let mut member_dtos = Vec::new();
|
||||
for member in members {
|
||||
match Self::get_user_info_with_privacy(
|
||||
&member.user_id.id.to_raw(),
|
||||
"system", // In member context, we show all user info
|
||||
true, // In member context, we show all user info
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut leader_dto) => {
|
||||
leader_dto.role = "leader".to_string();
|
||||
leader_dto
|
||||
Ok(mut member_dto) => {
|
||||
member_dto.role = member.role;
|
||||
member_dto.joined_at = member.joined_at;
|
||||
member_dtos.push(member_dto);
|
||||
}
|
||||
Err(_) => TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: team.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: team.created_at.clone(),
|
||||
}
|
||||
};
|
||||
|
||||
member_dtos.insert(0, leader_dto);
|
||||
|
||||
let team_dto = MemberTeamsDetailItemDto {
|
||||
id: team.id.id.to_raw(),
|
||||
name: team.name,
|
||||
description: team.description,
|
||||
leader: leader_dto,
|
||||
is_open: team.is_open,
|
||||
max_members: team.max_members,
|
||||
current_member_count: members.len() as i32 + 1,
|
||||
skills_required: team.skills_required,
|
||||
location: team.location,
|
||||
avatar: team.avatar,
|
||||
website_url: team.website_url,
|
||||
github_url: team.github_url,
|
||||
members: member_dtos,
|
||||
is_active: team.is_active,
|
||||
created_at: team.created_at,
|
||||
updated_at: team.updated_at,
|
||||
};
|
||||
success_response(ResponseSuccessDto { data: team_dto })
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||
|
||||
// Add leader with full info
|
||||
let leader_dto = match Self::get_user_info_with_privacy(
|
||||
&team.leader_id.id.to_raw(),
|
||||
"system",
|
||||
true,
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut leader_dto) => {
|
||||
leader_dto.role = "leader".to_string();
|
||||
leader_dto
|
||||
}
|
||||
Err(_) => TeamMemberDto {
|
||||
id: String::new(),
|
||||
user_id: team.leader_id.id.to_raw(),
|
||||
fullname: String::new(),
|
||||
email: None,
|
||||
avatar: None,
|
||||
role: "leader".to_string(),
|
||||
skills: None,
|
||||
joined_at: team.created_at.clone(),
|
||||
}
|
||||
};
|
||||
|
||||
member_dtos.insert(0, leader_dto);
|
||||
|
||||
let team_dto = MemberTeamsDetailItemDto {
|
||||
id: team.id.id.to_raw(),
|
||||
name: team.name,
|
||||
description: team.description,
|
||||
leader: leader_dto,
|
||||
is_open: team.is_open,
|
||||
max_members: team.max_members,
|
||||
current_member_count: members.len() as i32 + 1,
|
||||
skills_required: team.skills_required,
|
||||
location: team.location,
|
||||
avatar: team.avatar,
|
||||
website_url: team.website_url,
|
||||
github_url: team.github_url,
|
||||
members: member_dtos,
|
||||
is_active: team.is_active,
|
||||
created_at: team.created_at,
|
||||
updated_at: team.updated_at,
|
||||
};
|
||||
success_response(ResponseSuccessDto { data: team_dto })
|
||||
}
|
||||
})
|
||||
}
|
||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_public_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
@@ -372,8 +372,8 @@ impl TeamsServiceTrait for TeamsService {
|
||||
match repo.query_create_invitation(invitation).await {
|
||||
Ok(_) => {
|
||||
let inviter_user = match users_repo.query_user_by_id(&make_thing_from_enum(ResourceEnum::Users, &claims.user_id)).await {
|
||||
Ok(user) => user,
|
||||
Err(_) => continue,
|
||||
Ok(user) => user,
|
||||
Err(_) => continue,
|
||||
};
|
||||
if let Err(e) = Self::send_invitation_email(
|
||||
&team_schema.name,
|
||||
@@ -538,8 +538,8 @@ impl TeamsServiceTrait for TeamsService {
|
||||
match repo.query_create_invitation(invitation).await {
|
||||
Ok(_) => {
|
||||
let inviter_user = match users_repo.query_user_by_id(&make_thing_from_enum(ResourceEnum::Users, &claims.user_id)).await {
|
||||
Ok(user) => user,
|
||||
Err(_) => return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to get inviter user information"),
|
||||
Ok(user) => user,
|
||||
Err(_) => return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to get inviter user information"),
|
||||
};
|
||||
if let Err(e) = Self::send_invitation_email(
|
||||
&team.name,
|
||||
@@ -759,6 +759,11 @@ impl TeamsServiceTrait for TeamsService {
|
||||
};
|
||||
success_list_response(response)
|
||||
}
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_admin_team_list(state: &AppState, meta: MetaRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
@@ -777,129 +782,129 @@ impl TeamsServiceTrait for TeamsService {
|
||||
}
|
||||
|
||||
fn get_admin_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) if !team.is_deleted => {
|
||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||
let mut member_dtos = Vec::new();
|
||||
|
||||
for member in members {
|
||||
match Self::get_user_info_with_privacy(
|
||||
&member.user_id.id.to_raw(),
|
||||
"system", // Admin context - show all sensitive data
|
||||
true, // Admin context - always show sensitive data
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut member_dto) => {
|
||||
member_dto.role = member.role;
|
||||
member_dto.joined_at = member.joined_at;
|
||||
member_dtos.push(member_dto);
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
// Add leader with full sensitive info
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) if !team.is_deleted => {
|
||||
let members = repo.query_team_members(&team.id).await.unwrap_or_default();
|
||||
let mut member_dtos = Vec::new();
|
||||
|
||||
for member in members {
|
||||
match Self::get_user_info_with_privacy(
|
||||
&team.leader_id.id.to_raw(),
|
||||
"system",
|
||||
true,
|
||||
&member.user_id.id.to_raw(),
|
||||
"system", // Admin context - show all sensitive data
|
||||
true, // Admin context - always show sensitive data
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut leader_dto) => {
|
||||
leader_dto.role = "leader".to_string();
|
||||
member_dtos.insert(0, leader_dto);
|
||||
Ok(mut member_dto) => {
|
||||
member_dto.role = member.role;
|
||||
member_dto.joined_at = member.joined_at;
|
||||
member_dtos.push(member_dto);
|
||||
}
|
||||
Err(_) => {}
|
||||
Err(_) => continue,
|
||||
}
|
||||
|
||||
let team_dto = team.to_admin_detail_dto(member_dtos);
|
||||
success_response(ResponseSuccessDto { data: team_dto })
|
||||
}
|
||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_admin_team_members(state: &AppState, team_id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&team_id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||
|
||||
let members = match repo.query_team_members(&thing_id).await {
|
||||
Ok(members) => members,
|
||||
Err(e) => return common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
};
|
||||
|
||||
let mut member_dtos = Vec::new();
|
||||
for member in members {
|
||||
// Add leader with full sensitive info
|
||||
match Self::get_user_info_with_privacy(
|
||||
&member.user_id.id.to_raw(),
|
||||
"system", // Admin context - show all sensitive data
|
||||
true, // Admin context - always show sensitive data
|
||||
&team.leader_id.id.to_raw(),
|
||||
"system",
|
||||
true,
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut member_dto) => {
|
||||
member_dto.role = member.role;
|
||||
member_dto.joined_at = member.joined_at;
|
||||
member_dtos.push(member_dto);
|
||||
Ok(mut leader_dto) => {
|
||||
leader_dto.role = "leader".to_string();
|
||||
member_dtos.insert(0, leader_dto);
|
||||
}
|
||||
Err(_) => continue,
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
success_response(ResponseSuccessDto { data: member_dtos })
|
||||
})
|
||||
}
|
||||
|
||||
fn leave_current_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let user_thing = make_thing_from_enum(ResourceEnum::Users, &claims.user_id);
|
||||
|
||||
// Find the teams that the user is a member of
|
||||
let teams = match repo.query_teams_by_user(&user_thing).await {
|
||||
Ok(teams) => teams,
|
||||
Err(e) => {
|
||||
error!("Failed to query user teams: {}", e);
|
||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to retrieve user teams")
|
||||
},
|
||||
};
|
||||
|
||||
if teams.is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "User is not a member of any team");
|
||||
}
|
||||
|
||||
// For now, assume user is in only one team (common case)
|
||||
// In a future enhancement, we could ask the user to specify which team to leave
|
||||
let team = &teams[0];
|
||||
let team_id = team.id.id.to_raw();
|
||||
let team_thing = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||
|
||||
// Check if user is the leader
|
||||
if team.leader_id.id.to_raw() == claims.user_id {
|
||||
return common_response(StatusCode::FORBIDDEN, "Team leader cannot leave the team");
|
||||
}
|
||||
|
||||
match repo.query_remove_team_member(&team_thing, &user_thing).await {
|
||||
Ok(msg) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)),
|
||||
Err(e) => {
|
||||
error!("Failed to remove team member: {}", e);
|
||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
|
||||
},
|
||||
}
|
||||
})
|
||||
let team_dto = team.to_admin_detail_dto(member_dtos);
|
||||
success_response(ResponseSuccessDto { data: team_dto })
|
||||
}
|
||||
Ok(_) => common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_admin_team_members(state: &AppState, team_id: String) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&team_id).is_err() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||
|
||||
let members = match repo.query_team_members(&thing_id).await {
|
||||
Ok(members) => members,
|
||||
Err(e) => return common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
};
|
||||
|
||||
let mut member_dtos = Vec::new();
|
||||
for member in members {
|
||||
match Self::get_user_info_with_privacy(
|
||||
&member.user_id.id.to_raw(),
|
||||
"system", // Admin context - show all sensitive data
|
||||
true, // Admin context - always show sensitive data
|
||||
&state,
|
||||
).await {
|
||||
Ok(mut member_dto) => {
|
||||
member_dto.role = member.role;
|
||||
member_dto.joined_at = member.joined_at;
|
||||
member_dtos.push(member_dto);
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
success_response(ResponseSuccessDto { data: member_dtos })
|
||||
})
|
||||
}
|
||||
|
||||
fn leave_current_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let user_thing = make_thing_from_enum(ResourceEnum::Users, &claims.user_id);
|
||||
|
||||
// Find the teams that the user is a member of
|
||||
let teams = match repo.query_teams_by_user(&user_thing).await {
|
||||
Ok(teams) => teams,
|
||||
Err(e) => {
|
||||
error!("Failed to query user teams: {}", e);
|
||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to retrieve user teams")
|
||||
},
|
||||
};
|
||||
|
||||
if teams.is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "User is not a member of any team");
|
||||
}
|
||||
|
||||
// For now, assume user is in only one team (common case)
|
||||
// In a future enhancement, we could ask the user to specify which team to leave
|
||||
let team = &teams[0];
|
||||
let team_id = team.id.id.to_raw();
|
||||
let team_thing = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||
|
||||
// Check if user is the leader
|
||||
if team.leader_id.id.to_raw() == claims.user_id {
|
||||
return common_response(StatusCode::FORBIDDEN, "Team leader cannot leave the team");
|
||||
}
|
||||
|
||||
match repo.query_remove_team_member(&team_thing, &user_thing).await {
|
||||
Ok(msg) => common_response(StatusCode::OK, &format!("Successfully left team: {}", team.name)),
|
||||
Err(e) => {
|
||||
error!("Failed to remove team member: {}", e);
|
||||
return common_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to leave team")
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user