123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- package handlers
- import (
- "time"
- "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"
- "github.com/golang-jwt/jwt/v5"
- "github.com/google/uuid"
- "github.com/sirupsen/logrus"
- "golang.org/x/crypto/bcrypt"
- )
- type UserHandler struct {
- store *store.DataStore
- emailService smtp.EmailService
- jwtService *jwtutils.Service
- logger *logrus.Entry
- hook *hook.HookClient
- }
- 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.NewLogger("user-handler"),
- hook: hook,
- }
- }
- // 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"})
- return
- }
- user := &common.User{
- ID: uuid.New(),
- Email: req.Email,
- Name: req.Name,
- Role: "owner",
- Status: "pending",
- CreatedAt: time.Now(),
- UpdatedAt: time.Now(),
- }
- if err := h.store.CreateUser(c, user); err != nil {
- c.JSON(500, gin.H{"error": "Failed to create user"})
- return
- }
- c.JSON(201, user)
- }
- 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"})
- return
- }
- //check if the received email is already the one from the owner
- tmpOwner, err := h.store.GetUserByEmail(c, req.Email)
- if err != nil {
- c.JSON(500, gin.H{"error": "Failed to get user"})
- return
- }
- if tmpOwner.Email != req.Email {
- c.JSON(400, gin.H{"error": "Email does not match the owner"})
- return
- }
- hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
- user := &common.User{
- ID: tmpOwner.ID,
- Email: tmpOwner.Email,
- Name: tmpOwner.Name,
- PhoneNumber: tmpOwner.PhoneNumber,
- Password: string(hashedPassword),
- PasswordCreated: true,
- Role: tmpOwner.Role,
- Status: "active",
- CreatedAt: tmpOwner.CreatedAt,
- UpdatedAt: time.Now(),
- }
- if err := h.store.UpdateUser(c, user); err != nil {
- c.JSON(500, gin.H{"error": "Failed to create user"})
- return
- }
- user.Password = ""
- c.JSON(201, user)
- }
- // 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"})
- return
- }
- invite, err := h.store.GetInvite(c, req.Token)
- if err != nil {
- c.JSON(400, gin.H{"error": "Invalid invitation"})
- return
- }
- tx := h.store.BeginTx()
- hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
- user := &common.User{
- ID: uuid.New(),
- Email: req.Email,
- Name: req.Name,
- PhoneNumber: req.PhoneNumber,
- Password: string(hashedPassword),
- PasswordCreated: true,
- Role: invite.Role,
- Status: "active",
- CreatedAt: time.Now(),
- UpdatedAt: time.Now(),
- }
- if err := h.store.CreateUserTx(c, tx, user); err != nil {
- tx.Rollback()
- c.JSON(500, gin.H{"error": "Failed to create user"})
- return
- }
- //add user to workspace
- 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
- }
- //update invite status
- invite.Status = "accepted"
- if err := h.store.UpdateInviteStatusTx(c, tx, invite); err != nil {
- tx.Rollback()
- c.JSON(500, gin.H{"error": "Failed to update invitation"})
- return
- }
- if err := tx.Commit().Error; err != nil {
- c.JSON(500, gin.H{"error": "Failed to complete registration"})
- return
- }
- user.Password = ""
- resp := common.InvitationAcceptResponse{
- User: *user,
- WorkspaceID: invite.WorkspaceID,
- }
- c.JSON(201, resp)
- }
- func (h *UserHandler) ValidateInvitation(c *gin.Context) {
- //request token from query
- token := c.Query("token")
- h.logger.WithField("token", token).Info("Validating invitation")
- inv, err := h.store.GetInvite(c, token)
- if err != nil {
- c.JSON(400, gin.H{"error": "Invalid invitation"})
- return
- }
- //validate token
- _, err = h.jwtService.ValidateToken(token)
- if err != nil {
- if err == jwt.ErrTokenExpired {
- //update invite status
- inv.Status = "expired"
- if err := h.store.UpdateInvite(c, inv); err != nil {
- c.JSON(500, gin.H{"error": "Failed to update invitation"})
- return
- }
- c.JSON(400, gin.H{"error": "Invalid token"})
- return
- }
- }
- response := common.InvitationValidateResponse{
- Valid: true,
- WorkspaceID: inv.WorkspaceID,
- Email: inv.Email,
- }
- c.JSON(200, response)
- }
- 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
- }
- //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,
- WorkspaceID: req.WorkspaceID,
- 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.WorkspaceID.String()); err != nil {
- c.JSON(500, gin.H{"error": "Failed to send email"})
- return
- }
- 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"})
- return
- }
- email := h.jwtService.ExtractEmailFromClaims(claims)
- if email == "" {
- c.JSON(500, gin.H{"error": "Failed to get user email"})
- return
- }
- user, err := h.store.GetUserByEmail(c, email)
- if err != nil {
- c.JSON(500, gin.H{"error": "Failed to get user"})
- return
- }
- workspaces, err := h.store.GetWorkspacesByUserID(c, user.ID)
- if err != nil {
- c.JSON(500, gin.H{"error": "Failed to get user workspace role"})
- return
- }
- resp := common.UserResponse{
- User: *user,
- Workspaces: workspaces,
- }
- c.JSON(200, resp)
- }
- 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
- }
- // 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"})
- return
- }
- email := h.jwtService.ExtractEmailFromClaims(claims)
- if email == "" {
- c.JSON(500, gin.H{"error": "Failed to get user email"})
- return
- }
- user, err := h.store.GetUserByEmail(c, email)
- if err != nil {
- c.JSON(500, gin.H{"error": "Failed to get user"})
- 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"})
- return
- }
- c.JSON(200, gin.H{"message": "User added to workspace"})
- }
- func (h *UserHandler) CancelInvitation(c *gin.Context) {
- // Implementation
- }
|