package user import ( "RenewCMS/api/pkg/apperr" "RenewCMS/api/pkg/claims" validator "RenewCMS/api/pkg/validator" "RenewCMS/internal/domain/invite" "RenewCMS/internal/domain/user" "RenewCMS/internal/infrastructure/useCases" "encoding/json" "errors" "net/http" "time" ) type InviteHandler struct { CreateInviteUseCase *useCases.CreateInviteUseCase ListUsersAndInvitesUseCase *useCases.ListUsersAndInvitesUseCase } type CreateInviteRequest struct { Email string `json:"email" validate:"required,inviteemail"` } type inviteResponse struct { ID uint32 `json:"id"` Kind string `json:"kind"` Email string `json:"email"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` ExpiresAt time.Time `json:"expires_at"` } type inviteListItem struct { ID uint32 `json:"id"` Kind string `json:"kind"` Email string `json:"email"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } type userListItem struct { ID uint32 `json:"id"` Kind string `json:"kind"` Username string `json:"username"` DisplayName string `json:"display_name"` Email string `json:"email"` Role string `json:"role"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } func NewInviteHandler(createInvite *useCases.CreateInviteUseCase, listUsersAndInvites *useCases.ListUsersAndInvitesUseCase) *InviteHandler { return &InviteHandler{ CreateInviteUseCase: createInvite, ListUsersAndInvitesUseCase: listUsersAndInvites, } } func (h *InviteHandler) CreateInvite(w http.ResponseWriter, r *http.Request) { var request CreateInviteRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } if err := validator.Validate.Struct(request); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } userId, ok := claims.UserId(r) if !ok { http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) return } created, err := h.CreateInviteUseCase.CreateInvite(useCases.CreateInviteCommand{ Email: request.Email, InvitedBy: userId, }) if err != nil { writeCreateInviteError(w, err) return } body, err := json.Marshal(inviteResponse{ ID: created.ID, Kind: useCases.EntryKindInvite, Email: created.Email, Status: string(created.StatusAt(time.Now())), CreatedAt: created.CreatedAt, ExpiresAt: created.ExpiresAt, }) if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return } w.Header().Set("Cache-Control", "no-store") w.WriteHeader(http.StatusCreated) _, _ = w.Write(body) } func writeCreateInviteError(w http.ResponseWriter, err error) { switch { case errors.Is(err, invite.ErrEmailHasAccount): http.Error(w, apperr.EmailHasAccountErrorMessage, http.StatusConflict) case errors.Is(err, invite.ErrPendingInvite): http.Error(w, apperr.PendingInviteErrorMessage, http.StatusConflict) case errors.Is(err, invite.ErrInviteEmailNotSent): http.Error(w, apperr.InviteNotSentErrorMessage, http.StatusInternalServerError) case errors.Is(err, user.ErrUserNotFound): http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized) default: http.Error(w, "Internal server error", http.StatusInternalServerError) } } func (h *InviteHandler) ListUsers(w http.ResponseWriter, _ *http.Request) { entries := h.ListUsersAndInvitesUseCase.ListUsersAndInvites() items := make([]any, 0, len(entries)) for _, entry := range entries { if entry.Kind == useCases.EntryKindInvite { items = append(items, inviteListItem{entry.ID, entry.Kind, entry.Email, entry.Status, entry.CreatedAt}) continue } items = append(items, userListItem{entry.ID, entry.Kind, entry.Username, entry.DisplayName, entry.Email, entry.Role, entry.Status, entry.CreatedAt}) } body, err := json.Marshal(items) if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return } w.Header().Set("Cache-Control", "no-store") _, _ = w.Write(body) }