ultisuite-backend/internal/api/admin/user_bulk.go
R3D347HR4Y 621b0099d6
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(deploy): enhance Nginx configuration and API integration for UltiAI
- Updated .env.example to include new configuration options for the UltiAI branding and API endpoints.
- Enhanced Nginx configuration to support new API routes for the MCP and WebSocket connections.
- Introduced sub-filters for branding adjustments in Nginx responses.
- Added new JavaScript patch for API endpoint adjustments.
- Implemented tests for new API functionalities and improved error handling in the AI gateway.
2026-06-15 00:22:23 +02:00

132 lines
3.5 KiB
Go

package admin
import (
"context"
"errors"
"fmt"
"strings"
platformusers "github.com/ultisuite/ulti-backend/internal/users"
)
type bulkUsersRequest struct {
UserIDs []string `json:"user_ids"`
Action string `json:"action"`
Role string `json:"role"`
GroupID string `json:"group_id"`
}
type bulkUsersResult struct {
SuccessCount int `json:"success_count"`
Failed []bulkUserFailure `json:"failed,omitempty"`
}
type bulkUserFailure struct {
UserID string `json:"user_id"`
Message string `json:"message"`
}
func (s *Service) BulkUsersAction(ctx context.Context, actorSub string, req bulkUsersRequest) (bulkUsersResult, error) {
action := strings.ToLower(strings.TrimSpace(req.Action))
userIDs := dedupeNonEmpty(req.UserIDs)
if len(userIDs) == 0 {
return bulkUsersResult{}, fmt.Errorf("no user ids")
}
result := bulkUsersResult{}
switch action {
case "disable":
for _, userID := range userIDs {
if err := s.DisableUser(ctx, actorSub, userID); err != nil {
result.Failed = append(result.Failed, bulkUserFailure{UserID: userID, Message: bulkErrorMessage(err)})
continue
}
result.SuccessCount++
}
case "reactivate":
for _, userID := range userIDs {
if err := s.ReactivateUser(ctx, actorSub, userID); err != nil {
result.Failed = append(result.Failed, bulkUserFailure{UserID: userID, Message: bulkErrorMessage(err)})
continue
}
result.SuccessCount++
}
case "delete":
for _, userID := range userIDs {
if err := s.DeleteUser(ctx, actorSub, userID); err != nil {
result.Failed = append(result.Failed, bulkUserFailure{UserID: userID, Message: bulkErrorMessage(err)})
continue
}
result.SuccessCount++
}
case "set_role":
role := strings.TrimSpace(req.Role)
for _, userID := range userIDs {
if _, err := s.SetUserRole(ctx, actorSub, userID, role); err != nil {
result.Failed = append(result.Failed, bulkUserFailure{UserID: userID, Message: bulkErrorMessage(err)})
continue
}
result.SuccessCount++
}
case "add_to_group":
groupID := strings.TrimSpace(req.GroupID)
if groupID == "" {
return bulkUsersResult{}, fmt.Errorf("group_id required")
}
if err := s.AddUsersToGroup(ctx, actorSub, groupID, userIDs); err != nil {
return bulkUsersResult{}, err
}
result.SuccessCount = len(userIDs)
case "remove_from_group":
groupID := strings.TrimSpace(req.GroupID)
if groupID == "" {
return bulkUsersResult{}, fmt.Errorf("group_id required")
}
if err := s.RemoveUsersFromGroup(ctx, actorSub, groupID, userIDs); err != nil {
return bulkUsersResult{}, err
}
result.SuccessCount = len(userIDs)
default:
return bulkUsersResult{}, fmt.Errorf("invalid action")
}
s.logAudit(ctx, actorSub, "bulk_users_action", map[string]any{
"action": action,
"success_count": result.SuccessCount,
"failed_count": len(result.Failed),
"group_id": strings.TrimSpace(req.GroupID),
"role": strings.TrimSpace(req.Role),
})
return result, nil
}
func bulkErrorMessage(err error) string {
if errors.Is(err, ErrNotFound) {
return "not found"
}
if errors.Is(err, platformusers.ErrLastPlatformAdmin) {
return "cannot remove the last platform admin"
}
if err == nil {
return ""
}
return err.Error()
}
func dedupeNonEmpty(ids []string) []string {
seen := make(map[string]struct{}, len(ids))
out := make([]string, 0, len(ids))
for _, id := range ids {
id = strings.TrimSpace(id)
if id == "" {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
out = append(out, id)
}
return out
}