Merge pull request #1178 from trheyi/main
Refactor settings handling in user invitation, member, and team APIs
This commit is contained in:
commit
110abd5161
4 changed files with 141 additions and 82 deletions
|
|
@ -194,28 +194,20 @@ func GinInvitationCreate(c *gin.Context) {
|
||||||
"expiry": req.Expiry,
|
"expiry": req.Expiry,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add send_email setting from top-level field
|
// Prepare settings
|
||||||
if req.SendEmail != nil {
|
settings := &InvitationSettings{}
|
||||||
if invitationData["settings"] == nil {
|
if req.Settings != nil {
|
||||||
invitationData["settings"] = make(map[string]interface{})
|
settings = req.Settings
|
||||||
}
|
|
||||||
if settings, ok := invitationData["settings"].(map[string]interface{}); ok {
|
|
||||||
settings["send_email"] = *req.SendEmail
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add other settings if provided
|
// Add send_email from top-level field (for backward compatibility)
|
||||||
if req.Settings != nil {
|
if req.SendEmail != nil {
|
||||||
if invitationData["settings"] == nil {
|
settings.SendEmail = *req.SendEmail
|
||||||
invitationData["settings"] = req.Settings
|
}
|
||||||
} else {
|
|
||||||
// Merge settings
|
// Add settings to invitation data
|
||||||
if existingSettings, ok := invitationData["settings"].(map[string]interface{}); ok {
|
if settings.SendEmail || settings.Locale != "" {
|
||||||
for k, v := range req.Settings {
|
invitationData["settings"] = settings
|
||||||
existingSettings[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call business logic
|
// Call business logic
|
||||||
|
|
@ -734,8 +726,11 @@ func invitationCreate(ctx context.Context, userID, teamID string, invitationData
|
||||||
|
|
||||||
// Check send_email requirement early
|
// Check send_email requirement early
|
||||||
shouldSendEmail := false
|
shouldSendEmail := false
|
||||||
if settings, ok := invitationData["settings"].(map[string]interface{}); ok {
|
if settings, ok := invitationData["settings"].(*InvitationSettings); ok && settings != nil {
|
||||||
shouldSendEmail = toBool(settings["send_email"])
|
shouldSendEmail = settings.SendEmail
|
||||||
|
} else if settingsMap, ok := invitationData["settings"].(map[string]interface{}); ok {
|
||||||
|
// Fallback for map format (for backward compatibility)
|
||||||
|
shouldSendEmail = toBool(settingsMap["send_email"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// If send_email is true, email must be provided
|
// If send_email is true, email must be provided
|
||||||
|
|
@ -987,8 +982,13 @@ func getInvitationExpiry(invitationData maps.MapStrAny) (time.Duration, error) {
|
||||||
// Get team config expiry (from global config)
|
// Get team config expiry (from global config)
|
||||||
// Try to get locale from invitation data settings
|
// Try to get locale from invitation data settings
|
||||||
locale := "en"
|
locale := "en"
|
||||||
if settings, ok := invitationData["settings"].(map[string]interface{}); ok {
|
if settings, ok := invitationData["settings"].(*InvitationSettings); ok && settings != nil {
|
||||||
if loc := toString(settings["locale"]); loc != "" {
|
if settings.Locale != "" {
|
||||||
|
locale = settings.Locale
|
||||||
|
}
|
||||||
|
} else if settingsMap, ok := invitationData["settings"].(map[string]interface{}); ok {
|
||||||
|
// Fallback for map format (for backward compatibility)
|
||||||
|
if loc := toString(settingsMap["locale"]); loc != "" {
|
||||||
locale = loc
|
locale = loc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1020,8 +1020,13 @@ func sendInvitationEmail(ctx context.Context, email, inviterName, teamName, toke
|
||||||
|
|
||||||
// Get locale from invitation data settings
|
// Get locale from invitation data settings
|
||||||
locale := "en"
|
locale := "en"
|
||||||
if settings, ok := invitationData["settings"].(map[string]interface{}); ok {
|
if settings, ok := invitationData["settings"].(*InvitationSettings); ok && settings != nil {
|
||||||
if loc := toString(settings["locale"]); loc != "" {
|
if settings.Locale != "" {
|
||||||
|
locale = settings.Locale
|
||||||
|
}
|
||||||
|
} else if settingsMap, ok := invitationData["settings"].(map[string]interface{}); ok {
|
||||||
|
// Fallback for map format (for backward compatibility)
|
||||||
|
if loc := toString(settingsMap["locale"]); loc != "" {
|
||||||
locale = loc
|
locale = loc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1094,8 +1099,15 @@ func mapToInvitationResponse(data maps.MapStr) InvitationResponse {
|
||||||
|
|
||||||
// Add settings if available
|
// Add settings if available
|
||||||
if settings, ok := data["settings"]; ok {
|
if settings, ok := data["settings"]; ok {
|
||||||
if settingsMap, ok := settings.(map[string]interface{}); ok {
|
if invSettings, ok := settings.(*InvitationSettings); ok {
|
||||||
invitation.Settings = settingsMap
|
invitation.Settings = invSettings
|
||||||
|
} else if settingsMap, ok := settings.(map[string]interface{}); ok {
|
||||||
|
// Convert map to InvitationSettings
|
||||||
|
invSettings := &InvitationSettings{
|
||||||
|
SendEmail: toBool(settingsMap["send_email"]),
|
||||||
|
Locale: toString(settingsMap["locale"]),
|
||||||
|
}
|
||||||
|
invitation.Settings = invSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -797,8 +797,28 @@ func mapToMemberResponse(data maps.MapStr) MemberResponse {
|
||||||
|
|
||||||
// Add settings if available
|
// Add settings if available
|
||||||
if settings, ok := data["settings"]; ok {
|
if settings, ok := data["settings"]; ok {
|
||||||
if settingsMap, ok := settings.(map[string]interface{}); ok {
|
if memSettings, ok := settings.(*MemberSettings); ok {
|
||||||
member.Settings = settingsMap
|
member.Settings = memSettings
|
||||||
|
} else if settingsMap, ok := settings.(map[string]interface{}); ok {
|
||||||
|
// Convert map to MemberSettings (for backward compatibility)
|
||||||
|
memSettings := &MemberSettings{
|
||||||
|
Notifications: toBool(settingsMap["notifications"]),
|
||||||
|
}
|
||||||
|
// Handle permissions array
|
||||||
|
if perms, ok := settingsMap["permissions"]; ok {
|
||||||
|
if permsSlice, ok := perms.([]interface{}); ok {
|
||||||
|
permissions := make([]string, 0, len(permsSlice))
|
||||||
|
for _, p := range permsSlice {
|
||||||
|
if permStr, ok := p.(string); ok {
|
||||||
|
permissions = append(permissions, permStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memSettings.Permissions = permissions
|
||||||
|
} else if permsStrSlice, ok := perms.([]string); ok {
|
||||||
|
memSettings.Permissions = permsStrSlice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
member.Settings = memSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -818,8 +818,15 @@ func mapToTeamDetailResponse(data maps.MapStr) TeamDetailResponse {
|
||||||
|
|
||||||
// Add settings if available
|
// Add settings if available
|
||||||
if settings, ok := data["settings"]; ok {
|
if settings, ok := data["settings"]; ok {
|
||||||
if settingsMap, ok := settings.(map[string]interface{}); ok {
|
if teamSettings, ok := settings.(*TeamSettings); ok {
|
||||||
team.Settings = settingsMap
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,26 @@ const (
|
||||||
UserInfoSourceAccessToken = "access_token" // Extract user info from access token response
|
UserInfoSourceAccessToken = "access_token" // Extract user info from access token response
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ==== Settings Types ====
|
||||||
|
|
||||||
|
// TeamSettings represents team-specific settings
|
||||||
|
type TeamSettings struct {
|
||||||
|
Theme string `json:"theme,omitempty"` // Team UI theme (e.g., "light", "dark")
|
||||||
|
Visibility string `json:"visibility,omitempty"` // Team visibility (e.g., "public", "private")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberSettings represents member-specific settings
|
||||||
|
type MemberSettings struct {
|
||||||
|
Notifications bool `json:"notifications,omitempty"` // Whether to receive notifications
|
||||||
|
Permissions []string `json:"permissions,omitempty"` // Custom permissions (e.g., ["read", "write"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvitationSettings represents invitation-specific settings
|
||||||
|
type InvitationSettings struct {
|
||||||
|
SendEmail bool `json:"send_email,omitempty"` // Whether to send invitation email
|
||||||
|
Locale string `json:"locale,omitempty"` // Locale for email template
|
||||||
|
}
|
||||||
|
|
||||||
// ==== Team API Types ====
|
// ==== Team API Types ====
|
||||||
|
|
||||||
// TeamResponse represents a team in API responses
|
// TeamResponse represents a team in API responses
|
||||||
|
|
@ -222,40 +242,40 @@ type TeamResponse struct {
|
||||||
type TeamDetailResponse struct {
|
type TeamDetailResponse struct {
|
||||||
TeamResponse
|
TeamResponse
|
||||||
// Add additional fields that are only included in detailed responses
|
// Add additional fields that are only included in detailed responses
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *TeamSettings `json:"settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTeamRequest represents the request to create a team
|
// CreateTeamRequest represents the request to create a team
|
||||||
type CreateTeamRequest struct {
|
type CreateTeamRequest struct {
|
||||||
Name string `json:"name" binding:"required"`
|
Name string `json:"name" binding:"required"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *TeamSettings `json:"settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTeamRequest represents the request to update a team
|
// UpdateTeamRequest represents the request to update a team
|
||||||
type UpdateTeamRequest struct {
|
type UpdateTeamRequest struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *TeamSettings `json:"settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== Member API Types ====
|
// ==== Member API Types ====
|
||||||
|
|
||||||
// MemberResponse represents a team member in API responses
|
// MemberResponse represents a team member in API responses
|
||||||
type MemberResponse struct {
|
type MemberResponse struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
TeamID string `json:"team_id"`
|
TeamID string `json:"team_id"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
MemberType string `json:"member_type"`
|
MemberType string `json:"member_type"`
|
||||||
RoleID string `json:"role_id"`
|
RoleID string `json:"role_id"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
InvitedBy string `json:"invited_by,omitempty"`
|
InvitedBy string `json:"invited_by,omitempty"`
|
||||||
InvitedAt string `json:"invited_at,omitempty"`
|
InvitedAt string `json:"invited_at,omitempty"`
|
||||||
JoinedAt string `json:"joined_at,omitempty"`
|
JoinedAt string `json:"joined_at,omitempty"`
|
||||||
LastActivity string `json:"last_activity,omitempty"`
|
LastActivity string `json:"last_activity,omitempty"`
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *MemberSettings `json:"settings,omitempty"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
UpdatedAt string `json:"updated_at"`
|
UpdatedAt string `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemberDetailResponse represents detailed member information
|
// MemberDetailResponse represents detailed member information
|
||||||
|
|
@ -267,38 +287,38 @@ type MemberDetailResponse struct {
|
||||||
|
|
||||||
// CreateMemberRequest represents the request to add a member directly
|
// CreateMemberRequest represents the request to add a member directly
|
||||||
type CreateMemberRequest struct {
|
type CreateMemberRequest struct {
|
||||||
UserID string `json:"user_id" binding:"required"`
|
UserID string `json:"user_id" binding:"required"`
|
||||||
MemberType string `json:"member_type,omitempty"` // "user" or "robot"
|
MemberType string `json:"member_type,omitempty"` // "user" or "robot"
|
||||||
RoleID string `json:"role_id" binding:"required"`
|
RoleID string `json:"role_id" binding:"required"`
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *MemberSettings `json:"settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMemberRequest represents the request to update a member
|
// UpdateMemberRequest represents the request to update a member
|
||||||
type UpdateMemberRequest struct {
|
type UpdateMemberRequest struct {
|
||||||
RoleID string `json:"role_id,omitempty"`
|
RoleID string `json:"role_id,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *MemberSettings `json:"settings,omitempty"`
|
||||||
LastActivity string `json:"last_activity,omitempty"`
|
LastActivity string `json:"last_activity,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== Invitation API Types ====
|
// ==== Invitation API Types ====
|
||||||
|
|
||||||
// InvitationResponse represents a team invitation in API responses
|
// InvitationResponse represents a team invitation in API responses
|
||||||
type InvitationResponse struct {
|
type InvitationResponse struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
TeamID string `json:"team_id"`
|
TeamID string `json:"team_id"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
MemberType string `json:"member_type"`
|
MemberType string `json:"member_type"`
|
||||||
RoleID string `json:"role_id"`
|
RoleID string `json:"role_id"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
InvitedBy string `json:"invited_by"`
|
InvitedBy string `json:"invited_by"`
|
||||||
InvitedAt string `json:"invited_at"`
|
InvitedAt string `json:"invited_at"`
|
||||||
InvitationToken string `json:"invitation_token,omitempty"`
|
InvitationToken string `json:"invitation_token,omitempty"`
|
||||||
InvitationExpiresAt string `json:"invitation_expires_at,omitempty"`
|
InvitationExpiresAt string `json:"invitation_expires_at,omitempty"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *InvitationSettings `json:"settings,omitempty"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
UpdatedAt string `json:"updated_at"`
|
UpdatedAt string `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvitationDetailResponse represents detailed invitation information
|
// InvitationDetailResponse represents detailed invitation information
|
||||||
|
|
@ -311,14 +331,14 @@ type InvitationDetailResponse struct {
|
||||||
|
|
||||||
// CreateInvitationRequest represents the request to send a team invitation
|
// CreateInvitationRequest represents the request to send a team invitation
|
||||||
type CreateInvitationRequest struct {
|
type CreateInvitationRequest struct {
|
||||||
UserID string `json:"user_id,omitempty"` // Optional for unregistered users
|
UserID string `json:"user_id,omitempty"` // Optional for unregistered users
|
||||||
Email string `json:"email,omitempty"` // Email address (if not provided, will be read from user profile when user_id is provided)
|
Email string `json:"email,omitempty"` // Email address (if not provided, will be read from user profile when user_id is provided)
|
||||||
MemberType string `json:"member_type,omitempty"` // "user" or "robot"
|
MemberType string `json:"member_type,omitempty"` // "user" or "robot"
|
||||||
RoleID string `json:"role_id" binding:"required"`
|
RoleID string `json:"role_id" binding:"required"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Expiry string `json:"expiry,omitempty"` // Custom expiry duration (e.g., "1d", "8h"), defaults to team config
|
Expiry string `json:"expiry,omitempty"` // Custom expiry duration (e.g., "1d", "8h"), defaults to team config
|
||||||
SendEmail *bool `json:"send_email,omitempty"` // Whether to send email (defaults to false)
|
SendEmail *bool `json:"send_email,omitempty"` // Whether to send email (defaults to false)
|
||||||
Settings map[string]interface{} `json:"settings,omitempty"`
|
Settings *InvitationSettings `json:"settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== Team Configuration Types ====
|
// ==== Team Configuration Types ====
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue