2025-11-05 15:06:07 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
2025-11-05 15:25:41 +00:00
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
"attune-heart-therapy/internal/middleware"
|
|
|
|
|
"attune-heart-therapy/internal/services"
|
|
|
|
|
|
2025-11-05 15:06:07 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type AuthHandler struct {
|
2025-11-05 15:25:41 +00:00
|
|
|
userService services.UserService
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:25:41 +00:00
|
|
|
func NewAuthHandler(userService services.UserService) *AuthHandler {
|
|
|
|
|
return &AuthHandler{
|
|
|
|
|
userService: userService,
|
|
|
|
|
}
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:25:41 +00:00
|
|
|
// Register handles POST /api/auth/register for new user registration
|
2025-11-05 15:06:07 +00:00
|
|
|
func (h *AuthHandler) Register(c *gin.Context) {
|
2025-11-05 15:25:41 +00:00
|
|
|
var req services.RegisterRequest
|
|
|
|
|
|
|
|
|
|
// Bind JSON request to struct
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "Invalid request format",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create new user
|
|
|
|
|
user, err := h.userService.Register(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Check for specific error types
|
|
|
|
|
if err.Error() == "validation failed" ||
|
|
|
|
|
err.Error() == "password must be at least 8 characters long" {
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "Validation failed",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err.Error() == "user with email "+req.Email+" already exists" {
|
|
|
|
|
c.JSON(http.StatusConflict, gin.H{
|
|
|
|
|
"error": "User already exists",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
"error": "Failed to create user",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
|
|
|
"message": "User registered successfully",
|
|
|
|
|
"user": user,
|
|
|
|
|
})
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:25:41 +00:00
|
|
|
// Login handles POST /api/auth/login for user authentication
|
2025-11-05 15:06:07 +00:00
|
|
|
func (h *AuthHandler) Login(c *gin.Context) {
|
2025-11-05 15:25:41 +00:00
|
|
|
var loginReq struct {
|
|
|
|
|
Email string `json:"email" binding:"required,email"`
|
|
|
|
|
Password string `json:"password" binding:"required"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bind JSON request to struct
|
|
|
|
|
if err := c.ShouldBindJSON(&loginReq); err != nil {
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "Invalid request format",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Authenticate user
|
|
|
|
|
user, token, err := h.userService.Login(loginReq.Email, loginReq.Password)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err.Error() == "invalid credentials" {
|
|
|
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
|
|
|
"error": "Invalid email or password",
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
"error": "Login failed",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
"message": "Login successful",
|
|
|
|
|
"user": user,
|
|
|
|
|
"token": token,
|
|
|
|
|
})
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:25:41 +00:00
|
|
|
// GetProfile handles GET /api/auth/profile for retrieving user profile
|
2025-11-05 15:06:07 +00:00
|
|
|
func (h *AuthHandler) GetProfile(c *gin.Context) {
|
2025-11-05 15:25:41 +00:00
|
|
|
// Get user ID from JWT token (set by auth middleware)
|
|
|
|
|
userID, exists := middleware.GetUserIDFromContext(c)
|
|
|
|
|
if !exists {
|
|
|
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
|
|
|
"error": "User not authenticated",
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get user profile
|
|
|
|
|
user, err := h.userService.GetProfile(userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
|
|
|
"error": "User not found",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
"user": user,
|
|
|
|
|
})
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:25:41 +00:00
|
|
|
// UpdateProfile handles PUT /api/auth/profile for updating user profile
|
2025-11-05 15:06:07 +00:00
|
|
|
func (h *AuthHandler) UpdateProfile(c *gin.Context) {
|
2025-11-05 15:25:41 +00:00
|
|
|
// Get user ID from JWT token (set by auth middleware)
|
|
|
|
|
userID, exists := middleware.GetUserIDFromContext(c)
|
|
|
|
|
if !exists {
|
|
|
|
|
c.JSON(http.StatusUnauthorized, gin.H{
|
|
|
|
|
"error": "User not authenticated",
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var req services.UpdateProfileRequest
|
|
|
|
|
|
|
|
|
|
// Bind JSON request to struct
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "Invalid request format",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update user profile
|
|
|
|
|
user, err := h.userService.UpdateProfile(userID, req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err.Error() == "invalid user ID" {
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "Invalid user ID",
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
|
|
|
"error": "Failed to update profile",
|
|
|
|
|
"details": err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
"message": "Profile updated successfully",
|
|
|
|
|
"user": user,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Logout handles POST /api/auth/logout for user logout
|
|
|
|
|
// Note: Since we're using stateless JWT tokens, logout is handled client-side
|
|
|
|
|
// by removing the token. This endpoint is provided for consistency.
|
|
|
|
|
func (h *AuthHandler) Logout(c *gin.Context) {
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
|
"message": "Logout successful",
|
|
|
|
|
})
|
2025-11-05 15:06:07 +00:00
|
|
|
}
|