-
This commit is contained in:
52
user/internal/handlers/health_handler.go
Normal file
52
user/internal/handlers/health_handler.go
Normal file
@ -0,0 +1,52 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// HealthHandler handles health check requests
|
||||
type HealthHandler struct {
|
||||
db *sqlx.DB
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewHealthHandler creates a new health handler
|
||||
func NewHealthHandler(db *sqlx.DB, logger *zap.Logger) *HealthHandler {
|
||||
return &HealthHandler{
|
||||
db: db,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Health handles GET /health
|
||||
func (h *HealthHandler) Health(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"service": "user-service",
|
||||
"version": "1.0.0",
|
||||
})
|
||||
}
|
||||
|
||||
// Ready handles GET /ready
|
||||
func (h *HealthHandler) Ready(c *gin.Context) {
|
||||
// Check database connection
|
||||
if err := h.db.Ping(); err != nil {
|
||||
h.logger.Error("Database health check failed", zap.Error(err))
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||
"status": "not ready",
|
||||
"reason": "database connection failed",
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ready",
|
||||
"service": "user-service",
|
||||
"database": "connected",
|
||||
})
|
||||
}
|
||||
280
user/internal/handlers/user_handler.go
Normal file
280
user/internal/handlers/user_handler.go
Normal file
@ -0,0 +1,280 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/RyanCopley/skybridge/user/internal/domain"
|
||||
"github.com/RyanCopley/skybridge/user/internal/services"
|
||||
)
|
||||
|
||||
// UserHandler handles HTTP requests for user operations
|
||||
type UserHandler struct {
|
||||
userService services.UserService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUserHandler creates a new user handler
|
||||
func NewUserHandler(userService services.UserService, logger *zap.Logger) *UserHandler {
|
||||
return &UserHandler{
|
||||
userService: userService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create handles POST /users
|
||||
func (h *UserHandler) Create(c *gin.Context) {
|
||||
var req domain.CreateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Invalid request body", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Invalid request body",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get actor from context (set by auth middleware)
|
||||
actorID := getActorFromContext(c)
|
||||
|
||||
user, err := h.userService.Create(c.Request.Context(), &req, actorID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to create user", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to create user",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
}
|
||||
|
||||
// GetByID handles GET /users/:id
|
||||
func (h *UserHandler) GetByID(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
id, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Invalid user ID",
|
||||
"details": "User ID must be a valid UUID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.GetByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err.Error() == "user not found" {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "User not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get user", zap.String("id", id.String()), zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get user",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
// Update handles PUT /users/:id
|
||||
func (h *UserHandler) Update(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
id, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Invalid user ID",
|
||||
"details": "User ID must be a valid UUID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req domain.UpdateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("Invalid request body", zap.Error(err))
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Invalid request body",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get actor from context (set by auth middleware)
|
||||
actorID := getActorFromContext(c)
|
||||
|
||||
user, err := h.userService.Update(c.Request.Context(), id, &req, actorID)
|
||||
if err != nil {
|
||||
if err.Error() == "user not found" {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "User not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to update user", zap.String("id", id.String()), zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to update user",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
// Delete handles DELETE /users/:id
|
||||
func (h *UserHandler) Delete(c *gin.Context) {
|
||||
idParam := c.Param("id")
|
||||
id, err := uuid.Parse(idParam)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Invalid user ID",
|
||||
"details": "User ID must be a valid UUID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get actor from context (set by auth middleware)
|
||||
actorID := getActorFromContext(c)
|
||||
|
||||
err = h.userService.Delete(c.Request.Context(), id, actorID)
|
||||
if err != nil {
|
||||
if err.Error() == "user not found" {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "User not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to delete user", zap.String("id", id.String()), zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to delete user",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// List handles GET /users
|
||||
func (h *UserHandler) List(c *gin.Context) {
|
||||
var req domain.ListUsersRequest
|
||||
|
||||
// Parse query parameters
|
||||
if status := c.Query("status"); status != "" {
|
||||
s := domain.UserStatus(status)
|
||||
req.Status = &s
|
||||
}
|
||||
|
||||
if role := c.Query("role"); role != "" {
|
||||
r := domain.UserRole(role)
|
||||
req.Role = &r
|
||||
}
|
||||
|
||||
req.Search = c.Query("search")
|
||||
|
||||
if limit := c.Query("limit"); limit != "" {
|
||||
if l, err := strconv.Atoi(limit); err == nil && l > 0 {
|
||||
req.Limit = l
|
||||
}
|
||||
}
|
||||
|
||||
if offset := c.Query("offset"); offset != "" {
|
||||
if o, err := strconv.Atoi(offset); err == nil && o >= 0 {
|
||||
req.Offset = o
|
||||
}
|
||||
}
|
||||
|
||||
req.OrderBy = c.DefaultQuery("order_by", "created_at")
|
||||
req.OrderDir = c.DefaultQuery("order_dir", "desc")
|
||||
|
||||
response, err := h.userService.List(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to list users", zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to list users",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// GetByEmail handles GET /users/email/:email
|
||||
func (h *UserHandler) GetByEmail(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
if email == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Email parameter is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.GetByEmail(c.Request.Context(), email)
|
||||
if err != nil {
|
||||
if err.Error() == "user not found" {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "User not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
h.logger.Error("Failed to get user by email", zap.String("email", email), zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get user",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
// ExistsByEmail handles GET /users/exists/:email
|
||||
func (h *UserHandler) ExistsByEmail(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
if email == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Email parameter is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
exists, err := h.userService.ExistsByEmail(c.Request.Context(), email)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to check user existence", zap.String("email", email), zap.Error(err))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to check user existence",
|
||||
"details": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"exists": exists,
|
||||
"email": email,
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function to get actor from gin context
|
||||
func getActorFromContext(c *gin.Context) string {
|
||||
if actor, exists := c.Get("actor_id"); exists {
|
||||
if actorStr, ok := actor.(string); ok {
|
||||
return actorStr
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to email header if available
|
||||
if email := c.GetHeader("X-User-Email"); email != "" {
|
||||
return email
|
||||
}
|
||||
|
||||
return "system"
|
||||
}
|
||||
Reference in New Issue
Block a user