- Updated GetTeamsByMember method to include role information and owner status for each team member. - Refactored GinTeamList to utilize the new getUserTeams function, simplifying team retrieval logic and removing pagination. - Introduced getUserTeams and getUserTeamsCount functions to encapsulate team membership logic, improving code organization and clarity. - Removed deprecated team retrieval logic from user/team.go, streamlining the codebase.
821 lines
23 KiB
Go
821 lines
23 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/yaoapp/gou/model"
|
|
"github.com/yaoapp/gou/process"
|
|
"github.com/yaoapp/kun/exception"
|
|
"github.com/yaoapp/kun/log"
|
|
"github.com/yaoapp/kun/maps"
|
|
"github.com/yaoapp/yao/openapi/oauth"
|
|
"github.com/yaoapp/yao/openapi/oauth/providers/user"
|
|
"github.com/yaoapp/yao/openapi/response"
|
|
)
|
|
|
|
// Team Management Handlers
|
|
|
|
// GinTeamConfig handles GET /teams/config - Get team configuration (public)
|
|
func GinTeamConfig(c *gin.Context) {
|
|
locale := c.Query("locale")
|
|
if locale == "" {
|
|
locale = "en" // default locale
|
|
}
|
|
|
|
// Clean locale: remove whitespace and special characters
|
|
locale = strings.TrimSpace(locale)
|
|
locale = strings.Trim(locale, "?&=")
|
|
|
|
config := GetTeamConfig(locale)
|
|
if config == nil {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team configuration not found",
|
|
}
|
|
response.RespondWithError(c, response.StatusNotFound, errorResp)
|
|
return
|
|
}
|
|
|
|
response.RespondWithSuccess(c, http.StatusOK, config)
|
|
}
|
|
|
|
// GinTeamList handles GET /teams - Get user teams (all teams where user is a member)
|
|
func GinTeamList(c *gin.Context) {
|
|
// Get authorized user info
|
|
authInfo := oauth.GetAuthorizedInfo(c)
|
|
if authInfo == nil || authInfo.UserID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidClient.Code,
|
|
ErrorDescription: "User not authenticated",
|
|
}
|
|
response.RespondWithError(c, response.StatusUnauthorized, errorResp)
|
|
return
|
|
}
|
|
|
|
// Call business logic to get user teams with roles
|
|
teams, err := getUserTeams(c.Request.Context(), authInfo.UserID)
|
|
if err != nil {
|
|
log.Error("Failed to get user teams: %v", err)
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to retrieve teams",
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
return
|
|
}
|
|
|
|
// Return teams list directly (no pagination)
|
|
response.RespondWithSuccess(c, http.StatusOK, teams)
|
|
}
|
|
|
|
// GinTeamGet handles GET /teams/:id - Get user team details
|
|
func GinTeamGet(c *gin.Context) {
|
|
// Get authorized user info
|
|
authInfo := oauth.GetAuthorizedInfo(c)
|
|
if authInfo == nil || authInfo.UserID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidClient.Code,
|
|
ErrorDescription: "User not authenticated",
|
|
}
|
|
response.RespondWithError(c, response.StatusUnauthorized, errorResp)
|
|
return
|
|
}
|
|
|
|
teamID := c.Param("id")
|
|
if teamID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team ID is required",
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
log.Error("Failed to get user provider: %v", err)
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to initialize user provider",
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
return
|
|
}
|
|
|
|
// Get team details
|
|
teamData, err := provider.GetTeamDetail(c.Request.Context(), teamID)
|
|
if err != nil {
|
|
log.Error("Failed to get team details: %v", err)
|
|
// Check if it's a "team not found" error
|
|
if err.Error() == "team not found" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team not found",
|
|
}
|
|
response.RespondWithError(c, response.StatusNotFound, errorResp)
|
|
} else {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to retrieve team details",
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Check if user owns this team
|
|
ownerID := toString(teamData["owner_id"])
|
|
if ownerID != authInfo.UserID {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrAccessDenied.Code,
|
|
ErrorDescription: "Access denied: you don't own this team",
|
|
}
|
|
response.RespondWithError(c, response.StatusForbidden, errorResp)
|
|
return
|
|
}
|
|
|
|
// Convert to response format
|
|
team := mapToTeamDetailResponse(teamData)
|
|
response.RespondWithSuccess(c, http.StatusOK, team)
|
|
}
|
|
|
|
// GinTeamCreate handles POST /teams - Create user team
|
|
func GinTeamCreate(c *gin.Context) {
|
|
// Get authorized user info
|
|
authInfo := oauth.GetAuthorizedInfo(c)
|
|
if authInfo == nil || authInfo.UserID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidClient.Code,
|
|
ErrorDescription: "User not authenticated",
|
|
}
|
|
response.RespondWithError(c, response.StatusUnauthorized, errorResp)
|
|
return
|
|
}
|
|
|
|
// Parse request body
|
|
var req CreateTeamRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Invalid request body: " + err.Error(),
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
|
|
// Prepare team data
|
|
teamData := maps.MapStrAny{
|
|
"name": req.Name,
|
|
"description": req.Description,
|
|
}
|
|
|
|
// Add settings if provided
|
|
if req.Settings != nil {
|
|
teamData["settings"] = req.Settings
|
|
}
|
|
|
|
// Call business logic
|
|
teamID, err := teamCreate(c.Request.Context(), authInfo.UserID, teamData)
|
|
if err != nil {
|
|
log.Error("Failed to create team: %v", err)
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to create team",
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
return
|
|
}
|
|
|
|
// Get the created team details
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
log.Error("Failed to get user provider: %v", err)
|
|
// Return basic response if we can't get details
|
|
response.RespondWithSuccess(c, http.StatusCreated, gin.H{"team_id": teamID})
|
|
return
|
|
}
|
|
|
|
createdTeam, err := provider.GetTeamDetail(c.Request.Context(), teamID)
|
|
if err != nil {
|
|
log.Error("Failed to get created team details: %v", err)
|
|
// Return basic response if we can't get details
|
|
response.RespondWithSuccess(c, http.StatusCreated, gin.H{"team_id": teamID})
|
|
return
|
|
}
|
|
|
|
// Convert to response format
|
|
team := mapToTeamDetailResponse(createdTeam)
|
|
response.RespondWithSuccess(c, http.StatusCreated, team)
|
|
}
|
|
|
|
// GinTeamUpdate handles PUT /teams/:id - Update user team
|
|
func GinTeamUpdate(c *gin.Context) {
|
|
// Get authorized user info
|
|
authInfo := oauth.GetAuthorizedInfo(c)
|
|
if authInfo == nil || authInfo.UserID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidClient.Code,
|
|
ErrorDescription: "User not authenticated",
|
|
}
|
|
response.RespondWithError(c, response.StatusUnauthorized, errorResp)
|
|
return
|
|
}
|
|
|
|
teamID := c.Param("id")
|
|
if teamID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team ID is required",
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
|
|
// Parse request body
|
|
var req UpdateTeamRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Invalid request body: " + err.Error(),
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
|
|
// Prepare update data
|
|
updateData := maps.MapStrAny{}
|
|
|
|
if req.Name != "" {
|
|
updateData["name"] = req.Name
|
|
}
|
|
if req.Description != "" {
|
|
updateData["description"] = req.Description
|
|
}
|
|
if req.Settings != nil {
|
|
updateData["settings"] = req.Settings
|
|
}
|
|
|
|
// Call business logic
|
|
err := teamUpdate(c.Request.Context(), authInfo.UserID, teamID, updateData)
|
|
if err != nil {
|
|
log.Error("Failed to update team: %v", err)
|
|
// Check error type for appropriate response
|
|
if strings.Contains(err.Error(), "not found") {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team not found",
|
|
}
|
|
response.RespondWithError(c, response.StatusNotFound, errorResp)
|
|
} else if strings.Contains(err.Error(), "access denied") {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrAccessDenied.Code,
|
|
ErrorDescription: err.Error(),
|
|
}
|
|
response.RespondWithError(c, response.StatusForbidden, errorResp)
|
|
} else {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to update team",
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Get updated team details
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
log.Error("Failed to get user provider: %v", err)
|
|
response.RespondWithSuccess(c, http.StatusOK, gin.H{"message": "Team updated successfully"})
|
|
return
|
|
}
|
|
|
|
updatedTeam, err := provider.GetTeamDetail(c.Request.Context(), teamID)
|
|
if err != nil {
|
|
log.Error("Failed to get updated team details: %v", err)
|
|
response.RespondWithSuccess(c, http.StatusOK, gin.H{"message": "Team updated successfully"})
|
|
return
|
|
}
|
|
|
|
// Convert to response format
|
|
team := mapToTeamDetailResponse(updatedTeam)
|
|
response.RespondWithSuccess(c, http.StatusOK, team)
|
|
}
|
|
|
|
// GinTeamDelete handles DELETE /teams/:id - Delete user team
|
|
func GinTeamDelete(c *gin.Context) {
|
|
// Get authorized user info
|
|
authInfo := oauth.GetAuthorizedInfo(c)
|
|
if authInfo == nil || authInfo.UserID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidClient.Code,
|
|
ErrorDescription: "User not authenticated",
|
|
}
|
|
response.RespondWithError(c, response.StatusUnauthorized, errorResp)
|
|
return
|
|
}
|
|
|
|
teamID := c.Param("id")
|
|
if teamID == "" {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team ID is required",
|
|
}
|
|
response.RespondWithError(c, response.StatusBadRequest, errorResp)
|
|
return
|
|
}
|
|
|
|
// Call business logic
|
|
err := teamDelete(c.Request.Context(), authInfo.UserID, teamID)
|
|
if err != nil {
|
|
log.Error("Failed to delete team: %v", err)
|
|
// Check error type for appropriate response
|
|
if strings.Contains(err.Error(), "not found") {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrInvalidRequest.Code,
|
|
ErrorDescription: "Team not found",
|
|
}
|
|
response.RespondWithError(c, response.StatusNotFound, errorResp)
|
|
} else if strings.Contains(err.Error(), "access denied") {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrAccessDenied.Code,
|
|
ErrorDescription: err.Error(),
|
|
}
|
|
response.RespondWithError(c, response.StatusForbidden, errorResp)
|
|
} else {
|
|
errorResp := &response.ErrorResponse{
|
|
Code: response.ErrServerError.Code,
|
|
ErrorDescription: "Failed to delete team",
|
|
}
|
|
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
|
|
}
|
|
return
|
|
}
|
|
|
|
response.RespondWithSuccess(c, http.StatusOK, gin.H{"message": "Team deleted successfully"})
|
|
}
|
|
|
|
// Yao Process Handlers (for Yao application calls)
|
|
|
|
// ProcessTeamList user.team.list Team list processor
|
|
// Args[0] map: Query parameters {"status": "active", "name": "search", "page": 1, "pagesize": 20}
|
|
// Return: map: Paginated team list
|
|
func ProcessTeamList(process *process.Process) interface{} {
|
|
process.ValidateArgNums(1)
|
|
|
|
// Get user_id from session
|
|
userIDStr := GetUserIDFromSession(process)
|
|
|
|
// Parse query parameters
|
|
queryMap := process.ArgsMap(0)
|
|
|
|
// Build query parameters
|
|
param := model.QueryParam{}
|
|
|
|
// Add filters
|
|
if status, ok := queryMap["status"].(string); ok && status != "" {
|
|
param.Wheres = append(param.Wheres, model.QueryWhere{
|
|
Column: "status",
|
|
Value: status,
|
|
})
|
|
}
|
|
|
|
if name, ok := queryMap["name"].(string); ok && name != "" {
|
|
param.Wheres = append(param.Wheres, model.QueryWhere{
|
|
Column: "name",
|
|
Value: "%" + name + "%",
|
|
OP: "like",
|
|
})
|
|
}
|
|
|
|
// Parse pagination
|
|
page := 1
|
|
pagesize := 20
|
|
|
|
if p, ok := queryMap["page"]; ok {
|
|
if pageInt, ok := p.(int); ok && pageInt > 0 {
|
|
page = pageInt
|
|
}
|
|
}
|
|
|
|
if ps, ok := queryMap["pagesize"]; ok {
|
|
if pagesizeInt, ok := ps.(int); ok && pagesizeInt > 0 && pagesizeInt <= 100 {
|
|
pagesize = pagesizeInt
|
|
}
|
|
}
|
|
|
|
// Get context
|
|
ctx := process.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
// Call business logic
|
|
result, err := teamList(ctx, userIDStr, param, page, pagesize)
|
|
if err != nil {
|
|
exception.New("failed to list teams: %s", 500, err.Error()).Throw()
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ProcessTeamGet user.team.get Team get processor
|
|
// Args[0] string: team_id
|
|
// Return: map: Team details
|
|
func ProcessTeamGet(process *process.Process) interface{} {
|
|
process.ValidateArgNums(1)
|
|
|
|
// Get user_id from session
|
|
userIDStr := GetUserIDFromSession(process)
|
|
|
|
teamID := process.ArgsString(0)
|
|
if teamID == "" {
|
|
exception.New("team_id is required", 400).Throw()
|
|
}
|
|
|
|
// Get context
|
|
ctx := process.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
// Call business logic
|
|
result, err := teamGet(ctx, userIDStr, teamID)
|
|
if err != nil {
|
|
exception.New("failed to get team: %s", 500, err.Error()).Throw()
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ProcessTeamCreate user.team.create Team create processor
|
|
// Args[0] map: Team data {"name": "Team Name", "description": "Description", "settings": {...}}
|
|
// Return: map: {"team_id": "created_team_id"}
|
|
func ProcessTeamCreate(process *process.Process) interface{} {
|
|
process.ValidateArgNums(1)
|
|
|
|
// Get user_id from session
|
|
userIDStr := GetUserIDFromSession(process)
|
|
|
|
teamData := maps.MapStrAny(process.ArgsMap(0))
|
|
|
|
// Validate required fields
|
|
if _, ok := teamData["name"]; !ok {
|
|
exception.New("name is required", 400).Throw()
|
|
}
|
|
|
|
// Get context
|
|
ctx := process.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
// Call business logic
|
|
teamID, err := teamCreate(ctx, userIDStr, teamData)
|
|
if err != nil {
|
|
exception.New("failed to create team: %s", 500, err.Error()).Throw()
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"team_id": teamID,
|
|
}
|
|
}
|
|
|
|
// ProcessTeamUpdate user.team.update Team update processor
|
|
// Args[0] string: team_id
|
|
// Args[1] map: Update data {"name": "New Name", "description": "New Description", "settings": {...}}
|
|
// Return: map: {"message": "success"}
|
|
func ProcessTeamUpdate(process *process.Process) interface{} {
|
|
process.ValidateArgNums(2)
|
|
|
|
// Get user_id from session
|
|
userIDStr := GetUserIDFromSession(process)
|
|
|
|
teamID := process.ArgsString(0)
|
|
updateData := maps.MapStrAny(process.ArgsMap(1))
|
|
|
|
if teamID == "" {
|
|
exception.New("team_id is required", 400).Throw()
|
|
}
|
|
|
|
// Get context
|
|
ctx := process.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
// Call business logic
|
|
err := teamUpdate(ctx, userIDStr, teamID, updateData)
|
|
if err != nil {
|
|
exception.New("failed to update team: %s", 500, err.Error()).Throw()
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"message": "success",
|
|
}
|
|
}
|
|
|
|
// ProcessTeamDelete user.team.delete Team delete processor
|
|
// Args[0] string: team_id
|
|
// Return: map: {"message": "success"}
|
|
func ProcessTeamDelete(process *process.Process) interface{} {
|
|
process.ValidateArgNums(1)
|
|
|
|
// Get user_id from session
|
|
userIDStr := GetUserIDFromSession(process)
|
|
|
|
teamID := process.ArgsString(0)
|
|
if teamID == "" {
|
|
exception.New("team_id is required", 400).Throw()
|
|
}
|
|
|
|
// Get context
|
|
ctx := process.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
|
|
// Call business logic
|
|
err := teamDelete(ctx, userIDStr, teamID)
|
|
if err != nil {
|
|
exception.New("failed to delete team: %s", 500, err.Error()).Throw()
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"message": "success",
|
|
}
|
|
}
|
|
|
|
// Private Business Logic Functions (internal use only)
|
|
|
|
// teamList handles the business logic for listing user teams
|
|
func teamList(ctx context.Context, userID string, param model.QueryParam, page, pagesize int) (maps.MapStr, error) {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Add owner filter to query parameters
|
|
param.Wheres = append(param.Wheres, model.QueryWhere{
|
|
Column: "owner_id",
|
|
Value: userID,
|
|
})
|
|
|
|
// Set default ordering if not provided
|
|
if len(param.Orders) == 0 {
|
|
param.Orders = []model.QueryOrder{
|
|
{Column: "created_at", Option: "desc"},
|
|
}
|
|
}
|
|
|
|
// Get paginated teams
|
|
result, err := provider.PaginateTeams(ctx, param, page, pagesize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve teams: %w", err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// teamGet handles the business logic for getting a specific user team
|
|
func teamGet(ctx context.Context, userID, teamID string) (maps.MapStrAny, error) {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Get team details
|
|
teamData, err := provider.GetTeamDetail(ctx, teamID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve team details: %w", err)
|
|
}
|
|
|
|
// Check if user owns this team
|
|
ownerID := toString(teamData["owner_id"])
|
|
if ownerID != userID {
|
|
return nil, fmt.Errorf("access denied: user does not own this team")
|
|
}
|
|
|
|
return teamData, nil
|
|
}
|
|
|
|
// teamCreate handles the business logic for creating a user team
|
|
func teamCreate(ctx context.Context, userID string, teamData maps.MapStrAny) (string, error) {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Set owner and default values
|
|
teamData["owner_id"] = userID
|
|
teamData["status"] = "active"
|
|
teamData["is_verified"] = false
|
|
teamData["created_at"] = time.Now()
|
|
teamData["updated_at"] = time.Now()
|
|
|
|
// Create team
|
|
teamID, err := provider.CreateTeam(ctx, teamData)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create team: %w", err)
|
|
}
|
|
|
|
// Add the creator as an owner member of the team
|
|
ownerMemberData := maps.MapStrAny{
|
|
"team_id": teamID,
|
|
"user_id": userID,
|
|
"member_type": "user",
|
|
"role_id": "owner",
|
|
"status": "active",
|
|
"joined_at": time.Now(),
|
|
"created_at": time.Now(),
|
|
"updated_at": time.Now(),
|
|
}
|
|
|
|
_, err = provider.CreateMember(ctx, ownerMemberData)
|
|
if err != nil {
|
|
// Log the error but don't fail the team creation
|
|
log.Error("Failed to add owner as team member: %v", err)
|
|
// Consider whether to rollback team creation or continue
|
|
// For now, we'll continue as the team is already created
|
|
}
|
|
|
|
return teamID, nil
|
|
}
|
|
|
|
// teamUpdate handles the business logic for updating a user team
|
|
func teamUpdate(ctx context.Context, userID, teamID string, updateData maps.MapStrAny) error {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Check if team exists and user owns it
|
|
teamData, err := provider.GetTeam(ctx, teamID)
|
|
if err != nil {
|
|
return fmt.Errorf("team not found or access denied: %w", err)
|
|
}
|
|
|
|
// Check ownership
|
|
ownerID := toString(teamData["owner_id"])
|
|
if ownerID != userID {
|
|
return fmt.Errorf("access denied: user does not own this team")
|
|
}
|
|
|
|
// Add updated_at timestamp
|
|
updateData["updated_at"] = time.Now()
|
|
|
|
// Update team
|
|
err = provider.UpdateTeam(ctx, teamID, updateData)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update team: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// teamDelete handles the business logic for deleting a user team
|
|
func teamDelete(ctx context.Context, userID, teamID string) error {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Check if team exists and user owns it
|
|
teamData, err := provider.GetTeam(ctx, teamID)
|
|
if err != nil {
|
|
return fmt.Errorf("team not found or access denied: %w", err)
|
|
}
|
|
|
|
// Check ownership
|
|
ownerID := toString(teamData["owner_id"])
|
|
if ownerID != userID {
|
|
return fmt.Errorf("access denied: user does not own this team")
|
|
}
|
|
|
|
// First, remove all team members
|
|
err = provider.RemoveAllTeamMembers(ctx, teamID)
|
|
if err != nil {
|
|
// Log error but don't fail team deletion - members might not exist
|
|
log.Error("Failed to remove team members during team deletion: %v", err)
|
|
}
|
|
|
|
// Then delete the team
|
|
err = provider.DeleteTeam(ctx, teamID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete team: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Private Helper Functions (internal use only)
|
|
|
|
// getUserProvider gets the user provider from the global OAuth service
|
|
func getUserProvider() (*user.DefaultUser, error) {
|
|
// Check if global OAuth service is initialized
|
|
if oauth.OAuth == nil {
|
|
return nil, fmt.Errorf("OAuth service not initialized")
|
|
}
|
|
|
|
// Get user provider from OAuth service
|
|
userProvider, err := oauth.OAuth.GetUserProvider()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Type assert to DefaultUser (this should be safe based on the OAuth service implementation)
|
|
if defaultUser, ok := userProvider.(*user.DefaultUser); ok {
|
|
return defaultUser, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("user provider is not of type DefaultUser")
|
|
}
|
|
|
|
// mapToTeamResponse converts a map to TeamResponse
|
|
func mapToTeamResponse(data maps.MapStr) TeamResponse {
|
|
team := TeamResponse{
|
|
ID: toInt64(data["id"]),
|
|
TeamID: toString(data["team_id"]),
|
|
Name: toString(data["name"]),
|
|
Description: toString(data["description"]),
|
|
OwnerID: toString(data["owner_id"]),
|
|
Status: toString(data["status"]),
|
|
IsVerified: toBool(data["is_verified"]),
|
|
VerifiedBy: toString(data["verified_by"]),
|
|
VerifiedAt: toTimeString(data["verified_at"]),
|
|
CreatedAt: toTimeString(data["created_at"]),
|
|
UpdatedAt: toTimeString(data["updated_at"]),
|
|
}
|
|
|
|
return team
|
|
}
|
|
|
|
// mapToTeamDetailResponse converts a map to TeamDetailResponse
|
|
func mapToTeamDetailResponse(data maps.MapStr) TeamDetailResponse {
|
|
team := TeamDetailResponse{
|
|
TeamResponse: mapToTeamResponse(data),
|
|
}
|
|
|
|
// Add settings if available
|
|
if settings, ok := data["settings"]; ok {
|
|
if teamSettings, ok := settings.(*TeamSettings); ok {
|
|
team.Settings = teamSettings
|
|
} else if settingsMap, ok := settings.(map[string]interface{}); ok {
|
|
// Convert map to TeamSettings (for backward compatibility)
|
|
teamSettings := &TeamSettings{
|
|
Theme: toString(settingsMap["theme"]),
|
|
Visibility: toString(settingsMap["visibility"]),
|
|
}
|
|
team.Settings = teamSettings
|
|
}
|
|
}
|
|
|
|
return team
|
|
}
|
|
|
|
// Business Logic Functions for Team Membership
|
|
|
|
// getUserTeams gets all teams where the user is a member (includes role information)
|
|
func getUserTeams(ctx context.Context, userID string) ([]maps.MapStr, error) {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Get teams with role information
|
|
teams, err := provider.GetTeamsByMember(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve user teams: %w", err)
|
|
}
|
|
|
|
return teams, nil
|
|
}
|
|
|
|
// getUserTeamsCount counts the number of teams a user is a member of
|
|
func getUserTeamsCount(ctx context.Context, userID string) (int64, error) {
|
|
// Get user provider instance
|
|
provider, err := getUserProvider()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to get user provider: %w", err)
|
|
}
|
|
|
|
// Count teams
|
|
count, err := provider.CountTeamsByMember(ctx, userID)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to count user teams: %w", err)
|
|
}
|
|
|
|
return count, nil
|
|
}
|