|
@@ -6,6 +6,7 @@ import (
|
|
|
"git.linuxforward.com/byom/byom-core/common"
|
|
|
"git.linuxforward.com/byom/byom-core/hook"
|
|
|
"git.linuxforward.com/byom/byom-core/jwtutils"
|
|
|
+ "git.linuxforward.com/byom/byom-core/logger"
|
|
|
"git.linuxforward.com/byom/byom-core/smtp"
|
|
|
"git.linuxforward.com/byom/byom-core/store"
|
|
|
"github.com/gin-gonic/gin"
|
|
@@ -17,27 +18,61 @@ import (
|
|
|
|
|
|
type UserHandler struct {
|
|
|
store *store.DataStore
|
|
|
- emailService *smtp.Service
|
|
|
+ emailService smtp.EmailService
|
|
|
jwtService *jwtutils.Service
|
|
|
logger *logrus.Entry
|
|
|
hook *hook.HookClient
|
|
|
}
|
|
|
|
|
|
-func NewUserHandler(db *store.DataStore, emailSvc *smtp.Service, jwtSvc *jwtutils.Service, hook *hook.HookClient) *UserHandler {
|
|
|
-
|
|
|
- logger := logrus.WithField("core", "user-handler")
|
|
|
-
|
|
|
+func NewUserHandler(db *store.DataStore, emailSvc smtp.EmailService, jwtSvc *jwtutils.Service, hook *hook.HookClient) *UserHandler {
|
|
|
return &UserHandler{
|
|
|
store: db,
|
|
|
emailService: emailSvc,
|
|
|
jwtService: jwtSvc,
|
|
|
- logger: logger,
|
|
|
+ logger: logger.NewLogger("user-handler"),
|
|
|
hook: hook,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) InitWorkspaceOwner(c *gin.Context) {
|
|
|
- var req common.InitWorkspaceOwnerRequest
|
|
|
+// Auth
|
|
|
+func (h *UserHandler) Login(c *gin.Context) {
|
|
|
+ var req common.AuthLoginRequest
|
|
|
+ var resp common.AuthLoginResponse
|
|
|
+
|
|
|
+ if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
+ c.JSON(400, gin.H{"error": "Invalid request"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ user, err := h.store.GetUserByEmail(c, req.Email)
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(400, gin.H{"error": "Invalid email or password"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(400, gin.H{"error": "Invalid email or password"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ token, err := h.jwtService.GenerateToken(
|
|
|
+ user.Email,
|
|
|
+ user.Role,
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(500, gin.H{"error": "Failed to generate token"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ resp.Token = token
|
|
|
+
|
|
|
+ c.JSON(200, resp)
|
|
|
+}
|
|
|
+
|
|
|
+// Workspace Owner Management
|
|
|
+func (h *UserHandler) InitOwner(c *gin.Context) {
|
|
|
+ var req common.WorkspaceOwnerInitRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
c.JSON(400, gin.H{"error": "Invalid request"})
|
|
@@ -62,8 +97,8 @@ func (h *UserHandler) InitWorkspaceOwner(c *gin.Context) {
|
|
|
c.JSON(201, user)
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) CreateWorkspaceOwner(c *gin.Context) {
|
|
|
- var req common.CreateWorkspaceOwnerRequest
|
|
|
+func (h *UserHandler) CreateOwner(c *gin.Context) {
|
|
|
+ var req common.WorkspaceOwnerCreateRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
c.JSON(400, gin.H{"error": "Invalid request"})
|
|
@@ -104,8 +139,9 @@ func (h *UserHandler) CreateWorkspaceOwner(c *gin.Context) {
|
|
|
c.JSON(201, user)
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) CreateInvitedUser(c *gin.Context) {
|
|
|
- var req common.CreateInvitedUserRequest
|
|
|
+// Invitation Management
|
|
|
+func (h *UserHandler) AcceptInvitation(c *gin.Context) {
|
|
|
+ var req common.InvitationAcceptRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
c.JSON(400, gin.H{"error": "Invalid request"})
|
|
@@ -141,13 +177,7 @@ func (h *UserHandler) CreateInvitedUser(c *gin.Context) {
|
|
|
}
|
|
|
|
|
|
//add user to workspace
|
|
|
- workspaceID, err := uuid.Parse(invite.Workspace)
|
|
|
- if err != nil {
|
|
|
- tx.Rollback()
|
|
|
- c.JSON(400, gin.H{"error": "Invalid workspace ID"})
|
|
|
- return
|
|
|
- }
|
|
|
- if err := h.store.AddUserToWorkspaceTx(c, tx, invite.Role, user.ID, workspaceID); err != nil {
|
|
|
+ if err := h.store.AddUserToWorkspaceTx(c, tx, invite.Role, user.ID, invite.WorkspaceID); err != nil {
|
|
|
tx.Rollback()
|
|
|
c.JSON(500, gin.H{"error": "Failed to add user to workspace"})
|
|
|
return
|
|
@@ -169,68 +199,14 @@ func (h *UserHandler) CreateInvitedUser(c *gin.Context) {
|
|
|
|
|
|
user.Password = ""
|
|
|
|
|
|
- resp := common.CreateInvitedUserResponse{
|
|
|
- User: *user,
|
|
|
- Worspace: invite.Workspace,
|
|
|
+ resp := common.InvitationAcceptResponse{
|
|
|
+ User: *user,
|
|
|
+ WorkspaceID: invite.WorkspaceID,
|
|
|
}
|
|
|
c.JSON(201, resp)
|
|
|
}
|
|
|
|
|
|
-// Invite handlers
|
|
|
-func (h *UserHandler) InviteUser(c *gin.Context) {
|
|
|
- var req common.InviteUserRequest
|
|
|
-
|
|
|
- if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
- c.JSON(400, gin.H{"error": "Invalid request"})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- //check if user exists
|
|
|
- _, err := h.store.GetUserByEmail(c, req.Email)
|
|
|
- if err == nil {
|
|
|
- c.JSON(400, gin.H{"error": "User already exists"})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- //generate jwt token
|
|
|
- token, err := h.jwtService.GenerateToken(
|
|
|
- req.Email,
|
|
|
- req.Role,
|
|
|
- )
|
|
|
- if err != nil {
|
|
|
- c.JSON(500, gin.H{"error": "Failed to generate token"})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- //store token in db
|
|
|
- invite := &common.Invite{
|
|
|
- ID: uuid.New(),
|
|
|
- Email: req.Email,
|
|
|
- Role: req.Role,
|
|
|
- Workspace: req.Workspace,
|
|
|
- Token: token,
|
|
|
- Status: "pending",
|
|
|
- ExpiresAt: time.Now().Add(24 * time.Hour),
|
|
|
- CreatedAt: time.Now(),
|
|
|
- UpdatedAt: time.Now(),
|
|
|
- }
|
|
|
-
|
|
|
- //return invite
|
|
|
- if err := h.store.CreateInvite(c, invite); err != nil {
|
|
|
- c.JSON(500, gin.H{"error": "Failed to create invitation"})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- //send email with token
|
|
|
- if err := h.emailService.SendInviteEmail(req.Email, token, req.Workspace); err != nil {
|
|
|
- c.JSON(500, gin.H{"error": "Failed to send email"})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- c.JSON(201, invite)
|
|
|
-}
|
|
|
-
|
|
|
-func (h *UserHandler) ValidateInvitedUser(c *gin.Context) {
|
|
|
+func (h *UserHandler) ValidateInvitation(c *gin.Context) {
|
|
|
//request token from query
|
|
|
token := c.Query("token")
|
|
|
|
|
@@ -257,66 +233,75 @@ func (h *UserHandler) ValidateInvitedUser(c *gin.Context) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- response := common.ValidateInvitedUserRequest{
|
|
|
+ response := common.InvitationValidateResponse{
|
|
|
Valid: true,
|
|
|
- WorkspaceID: inv.Workspace,
|
|
|
+ WorkspaceID: inv.WorkspaceID,
|
|
|
Email: inv.Email,
|
|
|
}
|
|
|
|
|
|
c.JSON(200, response)
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) Login(c *gin.Context) {
|
|
|
-
|
|
|
- var req common.LoginRequest
|
|
|
- var resp common.LoginResponse
|
|
|
+func (h *UserHandler) CreateInvitation(c *gin.Context) {
|
|
|
+ var req common.InvitationCreateRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
c.JSON(400, gin.H{"error": "Invalid request"})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- user, err := h.store.GetUserByEmail(c, req.Email)
|
|
|
- if err != nil {
|
|
|
- c.JSON(400, gin.H{"error": "Invalid email or password"})
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
|
|
- if err != nil {
|
|
|
- c.JSON(400, gin.H{"error": "Invalid email or password"})
|
|
|
+ //check if user exists
|
|
|
+ _, err := h.store.GetUserByEmail(c, req.Email)
|
|
|
+ if err == nil {
|
|
|
+ c.JSON(400, gin.H{"error": "User already exists"})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ //generate jwt token
|
|
|
token, err := h.jwtService.GenerateToken(
|
|
|
- user.Email,
|
|
|
- user.Role,
|
|
|
+ req.Email,
|
|
|
+ req.Role,
|
|
|
)
|
|
|
if err != nil {
|
|
|
c.JSON(500, gin.H{"error": "Failed to generate token"})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- resp.Token = token
|
|
|
-
|
|
|
- c.JSON(200, resp)
|
|
|
-}
|
|
|
-
|
|
|
-func (h *UserHandler) AddUserToWorkspace(c *gin.Context) {
|
|
|
- var req common.AddUserToWorkspaceRequest
|
|
|
+ //store token in db
|
|
|
+ invite := &common.Invite{
|
|
|
+ ID: uuid.New(),
|
|
|
+ Email: req.Email,
|
|
|
+ Role: req.Role,
|
|
|
+ WorkspaceID: req.WorkspaceID,
|
|
|
+ Token: token,
|
|
|
+ Status: "pending",
|
|
|
+ ExpiresAt: time.Now().Add(24 * time.Hour),
|
|
|
+ CreatedAt: time.Now(),
|
|
|
+ UpdatedAt: time.Now(),
|
|
|
+ }
|
|
|
|
|
|
- if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
- c.JSON(400, gin.H{"error": "Invalid request"})
|
|
|
+ //return invite
|
|
|
+ if err := h.store.CreateInvite(c, invite); err != nil {
|
|
|
+ c.JSON(500, gin.H{"error": "Failed to create invitation"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+ //send email with token
|
|
|
+ if err := h.emailService.SendInviteEmail(req.Email, token, req.WorkspaceID.String()); err != nil {
|
|
|
+ c.JSON(500, gin.H{"error": "Failed to send email"})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- //retrieve user from token
|
|
|
+ c.JSON(201, invite)
|
|
|
+}
|
|
|
+
|
|
|
+// User Management
|
|
|
+func (h *UserHandler) GetCurrent(c *gin.Context) {
|
|
|
+ // Get claims from context that were set by middleware
|
|
|
claimsValue, exists := c.Get("claims")
|
|
|
if !exists {
|
|
|
c.JSON(500, gin.H{"error": "Failed to get user claims"})
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
claims := claimsValue.(jwt.MapClaims)
|
|
|
if claims == nil {
|
|
|
c.JSON(500, gin.H{"error": "Failed to get user claims"})
|
|
@@ -335,26 +320,46 @@ func (h *UserHandler) AddUserToWorkspace(c *gin.Context) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- //add user to workspace
|
|
|
- if err := h.store.AddUserToWorkspace(c, user.ID, req.WorkspaceID, req.Role); err != nil {
|
|
|
- c.JSON(500, gin.H{"error": "Failed to add user to workspace"})
|
|
|
+ workspaces, err := h.store.GetWorkspacesByUserID(c, user.ID)
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(500, gin.H{"error": "Failed to get user workspace role"})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- c.JSON(200, gin.H{"message": "User added to workspace"})
|
|
|
+ resp := common.UserResponse{
|
|
|
+ User: *user,
|
|
|
+ Workspaces: workspaces,
|
|
|
+ }
|
|
|
+
|
|
|
+ c.JSON(200, resp)
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) CancelInvitation(c *gin.Context) {
|
|
|
+func (h *UserHandler) UpdateCurrent(c *gin.Context) {
|
|
|
+ var req common.UserUpdateRequest
|
|
|
+
|
|
|
+ if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
+ c.JSON(400, gin.H{"error": "Invalid request"})
|
|
|
+ return
|
|
|
+ }
|
|
|
// Implementation
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) GetCurrentUser(c *gin.Context) {
|
|
|
- // Get claims from context that were set by middleware
|
|
|
+// Workspace Member Management
|
|
|
+func (h *UserHandler) AddMember(c *gin.Context) {
|
|
|
+ var req common.WorkspaceMemberAddRequest
|
|
|
+
|
|
|
+ if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
+ c.JSON(400, gin.H{"error": "Invalid request"})
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ //retrieve user from token
|
|
|
claimsValue, exists := c.Get("claims")
|
|
|
if !exists {
|
|
|
c.JSON(500, gin.H{"error": "Failed to get user claims"})
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
claims := claimsValue.(jwt.MapClaims)
|
|
|
if claims == nil {
|
|
|
c.JSON(500, gin.H{"error": "Failed to get user claims"})
|
|
@@ -373,21 +378,15 @@ func (h *UserHandler) GetCurrentUser(c *gin.Context) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- workspaces, err := h.store.GetWorkspacesByUserID(c, user.ID)
|
|
|
- if err != nil {
|
|
|
- c.JSON(500, gin.H{"error": "Failed to get user workspace role"})
|
|
|
+ //add user to workspace
|
|
|
+ if err := h.store.AddUserToWorkspace(c, user.ID, req.WorkspaceID, req.Role); err != nil {
|
|
|
+ c.JSON(500, gin.H{"error": "Failed to add user to workspace"})
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- resp := common.UserMe{
|
|
|
- User: *user,
|
|
|
- Workspaces: workspaces,
|
|
|
- }
|
|
|
-
|
|
|
- c.JSON(200, resp)
|
|
|
-
|
|
|
+ c.JSON(200, gin.H{"message": "User added to workspace"})
|
|
|
}
|
|
|
|
|
|
-func (h *UserHandler) UpdateCurrentUser(c *gin.Context) {
|
|
|
+func (h *UserHandler) CancelInvitation(c *gin.Context) {
|
|
|
// Implementation
|
|
|
}
|