mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
421 lines
11 KiB
Go
421 lines
11 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/api/middleware"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
)
|
|
|
|
// CreateOrganization creates a new organization
|
|
func (h *Handler) CreateOrganization(c *gin.Context) {
|
|
userID, err := uuid.Parse(middleware.GetUserID(c))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
var req models.CreateOrganizationRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
|
|
org, xerr := h.OrganizationService.Create(c.Request.Context(), userID, req.Name)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, org)
|
|
}
|
|
|
|
// GetUserOrganizations returns all organizations the user is a member of
|
|
func (h *Handler) GetUserOrganizations(c *gin.Context) {
|
|
userID, err := uuid.Parse(middleware.GetUserID(c))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
members, xerr := h.OrganizationService.GetUserOrganizations(c.Request.Context(), userID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": members})
|
|
}
|
|
|
|
// SwitchOrganization switches the current organization in the session
|
|
func (h *Handler) SwitchOrganization(c *gin.Context) {
|
|
userID, err := uuid.Parse(middleware.GetUserID(c))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
orgIDStr := c.Param("id")
|
|
orgID, err := uuid.Parse(orgIDStr)
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid organization ID"))
|
|
return
|
|
}
|
|
|
|
// Verify user is a member
|
|
member, xerr := h.OrganizationService.GetMembership(c.Request.Context(), orgID, userID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
if member == nil {
|
|
errx.JSON(c, errx.New(errx.Forbidden, "not a member of this organization"))
|
|
return
|
|
}
|
|
|
|
// Get session from context
|
|
session := middleware.GetSession(c)
|
|
if session == nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
// Update session with new organization
|
|
if xerr := h.TokenService.SwitchOrganization(c.Request.Context(), session.ID, &orgID); xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "organization switched",
|
|
"organization_id": orgID,
|
|
})
|
|
}
|
|
|
|
// GetCurrentOrganization returns the current organization from session
|
|
func (h *Handler) GetCurrentOrganization(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
org, xerr := h.OrganizationService.Get(c.Request.Context(), *orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
// Get counts and limits
|
|
counts, _ := h.OrganizationService.GetOrganizationCounts(c.Request.Context(), *orgID)
|
|
limits, _ := h.OrganizationService.GetOrganizationLimits(c.Request.Context(), *orgID)
|
|
|
|
result := models.OrganizationWithLimits{
|
|
Organization: *org,
|
|
Limits: limits,
|
|
Counts: counts,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
// UpdateOrganization updates the current organization
|
|
func (h *Handler) UpdateOrganization(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
var req models.UpdateOrganizationRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
|
|
org, xerr := h.OrganizationService.Update(c.Request.Context(), *orgID, &req)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, org)
|
|
}
|
|
|
|
// GetMembers returns all members of the current organization
|
|
func (h *Handler) GetMembers(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
members, xerr := h.OrganizationService.GetMembers(c.Request.Context(), *orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": members})
|
|
}
|
|
|
|
// InviteMember invites a new member to the organization
|
|
func (h *Handler) InviteMember(c *gin.Context) {
|
|
userID, err := uuid.Parse(middleware.GetUserID(c))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
var req models.InviteMemberRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
|
|
inv, xerr := h.OrganizationService.InviteMember(c.Request.Context(), *orgID, userID, &req)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
// Get organization name for email
|
|
org, _ := h.OrganizationService.Get(c.Request.Context(), *orgID)
|
|
orgName := "your organization"
|
|
if org != nil {
|
|
orgName = org.Name
|
|
}
|
|
|
|
// Get inviter name
|
|
inviter, _ := h.UserService.GetUser(c.Request.Context(), userID)
|
|
inviterName := "A team member"
|
|
if inviter != nil && inviter.FirstName != "" {
|
|
inviterName = inviter.FirstName
|
|
if inviter.LastName != "" {
|
|
inviterName += " " + inviter.LastName
|
|
}
|
|
}
|
|
|
|
// Send invitation email
|
|
if h.EmailNotificationService != nil {
|
|
subject := fmt.Sprintf("You've been invited to join %s on Warmbly", orgName)
|
|
body := fmt.Sprintf(`
|
|
<h2>You've been invited!</h2>
|
|
<p>%s has invited you to join <strong>%s</strong> on Warmbly.</p>
|
|
<p>Click the link below to accept the invitation:</p>
|
|
<p><a href="https://app.warmbly.com/invite?token=%s" style="display:inline-block;padding:12px 24px;background:#4F46E5;color:white;text-decoration:none;border-radius:6px;">Accept Invitation</a></p>
|
|
<p>This invitation expires in 7 days.</p>
|
|
<p>If you don't have an account yet, you'll be able to create one when you accept the invitation.</p>
|
|
`, inviterName, orgName, inv.Token)
|
|
|
|
go h.EmailNotificationService.Send(c.Request.Context(), []string{req.Email}, nil, nil, subject, body)
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"message": "invitation sent",
|
|
"invitation": inv,
|
|
})
|
|
}
|
|
|
|
// UpdateMemberRole updates a member's role and permissions
|
|
func (h *Handler) UpdateMemberRole(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
memberIDStr := c.Param("id")
|
|
memberUserID, err := uuid.Parse(memberIDStr)
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid member ID"))
|
|
return
|
|
}
|
|
|
|
var req models.UpdateMemberRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
|
|
member, xerr := h.OrganizationService.UpdateMemberRole(c.Request.Context(), *orgID, memberUserID, &req)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, member)
|
|
}
|
|
|
|
// RemoveMember removes a member from the organization
|
|
func (h *Handler) RemoveMember(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
memberIDStr := c.Param("id")
|
|
memberUserID, err := uuid.Parse(memberIDStr)
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid member ID"))
|
|
return
|
|
}
|
|
|
|
if xerr := h.OrganizationService.RemoveMember(c.Request.Context(), *orgID, memberUserID); xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "member removed"})
|
|
}
|
|
|
|
// TransferOwnership transfers organization ownership to another member
|
|
func (h *Handler) TransferOwnership(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
var req models.TransferOwnershipRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
|
|
if xerr := h.OrganizationService.TransferOwnership(c.Request.Context(), *orgID, req.NewOwnerUserID); xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "ownership transferred"})
|
|
}
|
|
|
|
// GetPendingInvitations returns pending invitations for the organization
|
|
func (h *Handler) GetPendingInvitations(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
invitations, xerr := h.OrganizationService.GetPendingInvitations(c.Request.Context(), *orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": invitations})
|
|
}
|
|
|
|
// CancelInvitation cancels a pending invitation
|
|
func (h *Handler) CancelInvitation(c *gin.Context) {
|
|
invIDStr := c.Param("id")
|
|
invID, err := uuid.Parse(invIDStr)
|
|
if err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid invitation ID"))
|
|
return
|
|
}
|
|
|
|
if xerr := h.OrganizationService.CancelInvitation(c.Request.Context(), invID); xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "invitation cancelled"})
|
|
}
|
|
|
|
// AcceptInvitation accepts an invitation (public endpoint - can be called before login or after)
|
|
func (h *Handler) AcceptInvitation(c *gin.Context) {
|
|
userID, err := uuid.Parse(middleware.GetUserID(c))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
var req models.AcceptInvitationRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "invalid request body"))
|
|
return
|
|
}
|
|
|
|
// Get user email from user service
|
|
user, xerr := h.UserService.GetUser(c.Request.Context(), userID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
member, xerr := h.OrganizationService.AcceptInvitation(c.Request.Context(), req.Token, userID, user.Email)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "invitation accepted",
|
|
"member": member,
|
|
})
|
|
}
|
|
|
|
// GetMyPendingInvitations returns pending invitations for the current user
|
|
func (h *Handler) GetMyPendingInvitations(c *gin.Context) {
|
|
userID, err := uuid.Parse(middleware.GetUserID(c))
|
|
if err != nil {
|
|
errx.JSON(c, errx.ErrUnauthorized)
|
|
return
|
|
}
|
|
|
|
// Get user email
|
|
user, xerr := h.UserService.GetUser(c.Request.Context(), userID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
invitations, xerr := h.OrganizationService.GetUserPendingInvitations(c.Request.Context(), user.Email)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": invitations})
|
|
}
|
|
|
|
// GetOrganizationLimits returns the organization's limits and current usage
|
|
func (h *Handler) GetOrganizationLimits(c *gin.Context) {
|
|
orgID := middleware.GetOrganizationID(c)
|
|
if orgID == nil {
|
|
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
|
|
return
|
|
}
|
|
|
|
limits, xerr := h.OrganizationService.GetOrganizationLimits(c.Request.Context(), *orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
counts, xerr := h.OrganizationService.GetOrganizationCounts(c.Request.Context(), *orgID)
|
|
if xerr != nil {
|
|
errx.JSON(c, xerr)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"limits": limits,
|
|
"counts": counts,
|
|
})
|
|
}
|