feat: Enhance hackathon submission and participant management
- Updated HackathonSubmissionsSchema to use Option types for team_id, project_name, description, technologies, submission_status, and submitted_at. - Modified seed_hackathons and seed_test_submission scripts to accommodate new optional fields. - Added routes for participant registration and listing in hackathon_controller. - Implemented register_participant and list_participants functions in hackathon_controller. - Introduced HackathonParticipantSchema and corresponding DTOs for participant management. - Enhanced HackathonRepository with CRUD operations for hackathon participants. - Updated HackathonService to include methods for participant registration and listing. - Refactored TeamsService to allow admin-level updates and invitations, bypassing leader-only restrictions. - Added validation for member emails in TeamsCreateRequestDto and TeamInviteRequestDto.
This commit is contained in:
@@ -150,7 +150,8 @@ pub async fn update_team(
|
||||
Json(payload): Json<TeamsUpdateRequestDto>,
|
||||
) -> impl IntoResponse {
|
||||
with_admin_perms(headers, Extension(state), move |claims, state| {
|
||||
TeamsService::update_team(&state, claims, id, payload)
|
||||
// Admin update should bypass leader-only restriction
|
||||
TeamsService::update_team_admin(&state, claims, id, payload)
|
||||
}).await
|
||||
}
|
||||
|
||||
@@ -174,7 +175,7 @@ pub async fn delete_team(
|
||||
Path(id): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
with_admin_perms(headers, Extension(state), move |claims, state| {
|
||||
TeamsService::delete_team(&state, claims, id)
|
||||
TeamsService::delete_team_admin(&state, claims, id)
|
||||
}).await
|
||||
}
|
||||
|
||||
@@ -200,7 +201,7 @@ pub async fn invite_team_members(
|
||||
Json(payload): Json<TeamInviteRequestDto>,
|
||||
) -> impl IntoResponse {
|
||||
with_admin_perms(headers, Extension(state), move |claims, state| {
|
||||
TeamsService::invite_team_members(&state, claims, team_id, payload)
|
||||
TeamsService::invite_team_members_admin(&state, claims, team_id, payload)
|
||||
}).await
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::{
|
||||
TeamMemberDto, TeamsSearchQueryDto, PublicTeamsListItemDto, PublicTeamsDetailItemDto,
|
||||
AdminTeamsListItemDto, AdminTeamsDetailItemDto, PermissionsEnum
|
||||
};
|
||||
use super::super::teams::{TeamsRepository, TeamMembersSchema};
|
||||
use axum::response::Response;
|
||||
use axum::extract::Path;
|
||||
use axum::http::HeaderMap;
|
||||
@@ -135,7 +136,104 @@ pub async fn put_update_team(
|
||||
Path(id): Path<String>,
|
||||
Json(payload): Json<TeamsUpdateRequestDto>,
|
||||
) -> impl IntoResponse {
|
||||
authenticated(headers, Extension(state), move |claims, state| TeamsService::update_team(&state, claims, id, payload)).await
|
||||
|
||||
// Try to treat this request as an admin first; if the caller has ManageAllTeams
|
||||
// permission, route to the admin update. Otherwise fall back to normal authenticated
|
||||
// update which enforces leader-only rules.
|
||||
let state_clone = state.clone();
|
||||
match crate::permissions_guard(headers.clone(), axum::Extension(state_clone.clone()), vec![PermissionsEnum::ManageAllTeams]).await {
|
||||
Ok((claims, state)) => {
|
||||
// Caller is admin
|
||||
TeamsService::update_team_admin(&state, claims, id, payload).await
|
||||
}
|
||||
Err(_) => {
|
||||
// Not admin - proceed with normal authenticated flow
|
||||
authenticated(headers, Extension(state), move |claims, state| TeamsService::update_team(&state, claims, id, payload)).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct AddTeamMemberRequestDto {
|
||||
pub user_id: String,
|
||||
pub role: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn post_add_team_member(
|
||||
headers: HeaderMap,
|
||||
Extension(state): Extension<AppState>,
|
||||
Path(team_id): Path<String>,
|
||||
Json(payload): Json<AddTeamMemberRequestDto>,
|
||||
) -> impl IntoResponse {
|
||||
// Determine caller and whether they have admin permissions
|
||||
let state_clone = state.clone();
|
||||
let is_admin = crate::permissions_guard(headers.clone(), axum::Extension(state_clone.clone()), vec![PermissionsEnum::ManageAllTeams]).await.is_ok();
|
||||
|
||||
// Authenticate the caller (will return 401 if no token)
|
||||
let auth = permissions_guard(headers, axum::Extension(state.clone()), vec![/* no specific perms */]).await;
|
||||
let (claims, state) = match auth {
|
||||
Ok((c, s)) => (c, s),
|
||||
Err(response) => return response,
|
||||
};
|
||||
|
||||
// Permission: admins can add anyone; otherwise only team leader or existing member can add
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = imphnen_utils::make_thing_from_enum(imphnen_libs::ResourceEnum::Teams, &team_id);
|
||||
let team = match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(t) => t,
|
||||
Err(_) => return crate::common_response(axum::http::StatusCode::NOT_FOUND, "Team not found"),
|
||||
};
|
||||
|
||||
if !is_admin {
|
||||
let user_thing = imphnen_utils::make_thing_from_enum(imphnen_libs::ResourceEnum::Users, &claims.user_id);
|
||||
let is_member = repo.query_is_team_member(&thing_id, &user_thing).await.unwrap_or(false);
|
||||
let is_leader = team.leader_id.id.to_raw() == claims.user_id;
|
||||
if !is_member && !is_leader {
|
||||
return crate::common_response(axum::http::StatusCode::FORBIDDEN, "Only team leader or members can add a member");
|
||||
}
|
||||
}
|
||||
|
||||
// Build member schema and add via repository
|
||||
let member_schema = TeamMembersSchema::create(team_id.clone(), payload.user_id.clone(), payload.role.clone());
|
||||
match repo.query_add_team_member(member_schema).await {
|
||||
Ok(msg) => crate::success_response(crate::ResponseSuccessDto { data: msg }),
|
||||
Err(e) => crate::common_response(axum::http::StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_remove_team_member(
|
||||
headers: HeaderMap,
|
||||
Extension(state): Extension<AppState>,
|
||||
Path((team_id, user_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
let state_clone = state.clone();
|
||||
let is_admin = crate::permissions_guard(headers.clone(), axum::Extension(state_clone.clone()), vec![PermissionsEnum::ManageAllTeams]).await.is_ok();
|
||||
|
||||
let auth = permissions_guard(headers, axum::Extension(state.clone()), vec![]).await;
|
||||
let (claims, state) = match auth {
|
||||
Ok((c, s)) => (c, s),
|
||||
Err(response) => return response,
|
||||
};
|
||||
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = imphnen_utils::make_thing_from_enum(imphnen_libs::ResourceEnum::Teams, &team_id);
|
||||
let team = match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(t) => t,
|
||||
Err(_) => return crate::common_response(axum::http::StatusCode::NOT_FOUND, "Team not found"),
|
||||
};
|
||||
|
||||
if !is_admin {
|
||||
// Only leader can remove members
|
||||
if team.leader_id.id.to_raw() != claims.user_id {
|
||||
return crate::common_response(axum::http::StatusCode::FORBIDDEN, "Only team leader can remove members");
|
||||
}
|
||||
}
|
||||
|
||||
let user_thing = imphnen_utils::make_thing_from_enum(imphnen_libs::ResourceEnum::Users, &user_id);
|
||||
match repo.query_remove_team_member(&thing_id, &user_thing).await {
|
||||
Ok(msg) => crate::success_response(crate::ResponseSuccessDto { data: msg }),
|
||||
Err(e) => crate::common_response(axum::http::StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
@@ -384,6 +482,8 @@ pub fn teams_router() -> Router {
|
||||
.route("/accept/{token}", axum::routing::post(post_accept_invitation))
|
||||
.route("/search", axum::routing::get(get_public_team_search))
|
||||
.route("/{id}/members", axum::routing::get(get_team_members))
|
||||
.route("/{id}/members", axum::routing::post(post_add_team_member))
|
||||
.route("/{id}/members/{user_id}", axum::routing::delete(delete_remove_team_member))
|
||||
.route("/{id}/leave", axum::routing::post(post_leave_team))
|
||||
.route("/leave-me", axum::routing::post(post_leave_current_team))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,28 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::sql::Thing;
|
||||
use utoipa::ToSchema;
|
||||
use validator::Validate;
|
||||
use validator::{Validate, ValidationError};
|
||||
use std::borrow::Cow;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
|
||||
// Custom validator for Vec<String> of emails. We use a custom validator because
|
||||
// the `each = true` attribute is not supported by the project's validator
|
||||
// crate version. This keeps validation at the DTO level as required.
|
||||
lazy_static! {
|
||||
static ref EMAIL_RE: Regex = Regex::new(r"^[^@\s]+@[^@\s]+\.[^@\s]+$").unwrap();
|
||||
}
|
||||
|
||||
fn validate_member_emails(emails: &Vec<String>) -> Result<(), ValidationError> {
|
||||
for email in emails {
|
||||
if !EMAIL_RE.is_match(email) {
|
||||
let mut err = ValidationError::new("invalid_email");
|
||||
err.message = Some(Cow::from("Invalid email"));
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
use imphnen_entities::users::UsersDetailQueryDto;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
@@ -39,7 +60,8 @@ pub struct TeamsCreateRequestDto {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub github_url: Option<String>,
|
||||
|
||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||
#[serde(default)]
|
||||
#[validate(custom(function = "validate_member_emails", message = "Invalid email in member_emails"))]
|
||||
pub member_emails: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -83,6 +105,7 @@ pub struct TeamsUpdateRequestDto {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||
pub struct TeamInviteRequestDto {
|
||||
#[validate(length(min = 1, message = "Member emails cannot be empty"))]
|
||||
#[validate(custom(function = "validate_member_emails", message = "Invalid email in member_emails"))]
|
||||
pub member_emails: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -455,3 +478,5 @@ impl TeamsDetailQueryDto {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (previous custom validator removed; using validator::email(each = true) attribute)
|
||||
|
||||
@@ -29,8 +29,11 @@ pub trait TeamsServiceTrait: Send + Sync + 'static {
|
||||
fn get_public_team_by_id(state: &AppState, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn create_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, new_team: TeamsCreateRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn update_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, id: String, team: TeamsUpdateRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn update_team_admin(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, id: String, team: TeamsUpdateRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn delete_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn delete_team_admin(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, id: String) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn invite_team_members(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, team_id: String, invite: TeamInviteRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn invite_team_members_admin(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, team_id: String, invite: TeamInviteRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn accept_invitation(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, accept: TeamAcceptInvitationRequestDto) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn get_team_members(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, team_id: String) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
fn leave_team(state: &AppState, claims: imphnen_libs::jsonwebtoken::Claims, team_id: String) -> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
@@ -128,7 +131,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
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() {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
@@ -195,7 +198,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
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() {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
@@ -303,7 +306,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
fn get_public_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() {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
@@ -416,7 +419,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
"failed_emails": failed_invites
|
||||
});
|
||||
|
||||
success_response(ResponseSuccessDto { data: response_data })
|
||||
imphnen_utils::success_created_response(ResponseSuccessDto { data: response_data })
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to create team: {}", err);
|
||||
@@ -434,7 +437,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
@@ -450,7 +453,9 @@ impl TeamsServiceTrait for TeamsService {
|
||||
Err(_) => return common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
};
|
||||
|
||||
// Allow update if requester is leader
|
||||
if current_team.leader_id.id.to_raw() != claims.user_id {
|
||||
// Not leader; deny here (admin endpoints should use update_team_admin)
|
||||
return common_response(StatusCode::FORBIDDEN, "Only team leader can update team");
|
||||
}
|
||||
|
||||
@@ -470,6 +475,45 @@ impl TeamsServiceTrait for TeamsService {
|
||||
})
|
||||
}
|
||||
|
||||
fn update_team_admin(
|
||||
state: &AppState,
|
||||
_claims: imphnen_libs::jsonwebtoken::Claims,
|
||||
id: String,
|
||||
team: TeamsUpdateRequestDto,
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
if let Err((status, message)) = validate_request(&team) {
|
||||
return common_response(status, &message);
|
||||
}
|
||||
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &id);
|
||||
let current_team = match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) => team,
|
||||
Err(_) => return common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
};
|
||||
|
||||
let updated_team = TeamsSchema {
|
||||
id: current_team.id,
|
||||
leader_id: current_team.leader_id,
|
||||
is_active: current_team.is_active,
|
||||
is_deleted: current_team.is_deleted,
|
||||
created_at: current_team.created_at,
|
||||
..TeamsSchema::default()
|
||||
}.update(team);
|
||||
|
||||
match repo.query_update_team(updated_team).await {
|
||||
Ok(msg) => common_response(StatusCode::OK, &msg),
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_team(
|
||||
state: &AppState,
|
||||
claims: imphnen_libs::jsonwebtoken::Claims,
|
||||
@@ -477,7 +521,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&id).is_err() {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
@@ -500,6 +544,31 @@ impl TeamsServiceTrait for TeamsService {
|
||||
})
|
||||
}
|
||||
|
||||
fn delete_team_admin(
|
||||
state: &AppState,
|
||||
_claims: imphnen_libs::jsonwebtoken::Claims,
|
||||
id: String,
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if id.trim().is_empty() {
|
||||
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);
|
||||
let _team = match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) => team,
|
||||
Err(_) => return common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
};
|
||||
|
||||
match repo.query_delete_team(id).await {
|
||||
Ok(msg) => common_response(StatusCode::OK, &msg),
|
||||
Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn invite_team_members(
|
||||
state: &AppState,
|
||||
claims: imphnen_libs::jsonwebtoken::Claims,
|
||||
@@ -508,7 +577,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&team_id).is_err() {
|
||||
if team_id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
@@ -584,6 +653,80 @@ impl TeamsServiceTrait for TeamsService {
|
||||
})
|
||||
}
|
||||
|
||||
fn invite_team_members_admin(
|
||||
state: &AppState,
|
||||
claims: imphnen_libs::jsonwebtoken::Claims,
|
||||
team_id: String,
|
||||
invite: TeamInviteRequestDto,
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if team_id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
if let Err((status, message)) = validate_request(&invite) {
|
||||
return common_response(status, &message);
|
||||
}
|
||||
|
||||
let repo = TeamsRepository::new(&state);
|
||||
let users_repo = UsersRepository::new(&state);
|
||||
let thing_id = make_thing_from_enum(ResourceEnum::Teams, &team_id);
|
||||
let team = match repo.query_team_by_id(&thing_id).await {
|
||||
Ok(team) => team,
|
||||
Err(_) => return common_response(StatusCode::NOT_FOUND, "Team not found"),
|
||||
};
|
||||
|
||||
let mut successful_invites = Vec::new();
|
||||
let mut failed_invites = Vec::new();
|
||||
|
||||
for email in invite.member_emails {
|
||||
let existing_user = users_repo.query_user_by_email(email.clone()).await.ok();
|
||||
let is_existing_user = existing_user.is_some();
|
||||
let token = Self::generate_invitation_token().await;
|
||||
let invitation = TeamInvitationsSchema::create(
|
||||
team_id.clone(),
|
||||
email.clone(),
|
||||
claims.user_id.clone(),
|
||||
token.clone(),
|
||||
);
|
||||
|
||||
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"),
|
||||
};
|
||||
if let Err(e) = Self::send_invitation_email(
|
||||
&team.name,
|
||||
&inviter_user.fullname,
|
||||
&email,
|
||||
&token,
|
||||
is_existing_user,
|
||||
).await {
|
||||
error!("Failed to send invitation email to {}: {}", email, e);
|
||||
failed_invites.push(email);
|
||||
} else {
|
||||
successful_invites.push(email);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to create invitation for {}: {}", email, e);
|
||||
failed_invites.push(email);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let response_data = json!({
|
||||
"invitations_sent": successful_invites.len(),
|
||||
"invitations_failed": failed_invites.len(),
|
||||
"failed_emails": failed_invites
|
||||
});
|
||||
|
||||
success_response(ResponseSuccessDto { data: response_data })
|
||||
})
|
||||
}
|
||||
|
||||
fn accept_invitation(
|
||||
state: &AppState,
|
||||
claims: imphnen_libs::jsonwebtoken::Claims,
|
||||
@@ -667,7 +810,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&team_id).is_err() {
|
||||
if team_id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
@@ -725,7 +868,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
|
||||
let state = state.to_owned();
|
||||
Box::pin(async move {
|
||||
if Uuid::parse_str(&team_id).is_err() {
|
||||
if team_id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
|
||||
@@ -794,7 +937,7 @@ 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() {
|
||||
if id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
@@ -844,7 +987,7 @@ impl TeamsServiceTrait for TeamsService {
|
||||
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() {
|
||||
if team_id.trim().is_empty() {
|
||||
return common_response(StatusCode::BAD_REQUEST, "Invalid Team ID format");
|
||||
}
|
||||
let repo = TeamsRepository::new(&state);
|
||||
|
||||
Reference in New Issue
Block a user