Boost Azure Demo
This commit is contained in:
133
Backend/internal/handlers/api/users/handler.go
Normal file
133
Backend/internal/handlers/api/users/handler.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Path: Backend/internal/handlers/api/users/handler.go
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"boostai-backend/internal/handlers/api/shared"
|
||||
"boostai-backend/internal/http/params"
|
||||
"boostai-backend/internal/http/respond"
|
||||
"boostai-backend/internal/sqlc"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
queries *sqlc.Queries
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
FullName string `json:"full_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||
PasswordHash *string `json:"password_hash,omitempty"`
|
||||
}
|
||||
|
||||
type createUserRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password *string `json:"password"`
|
||||
Role string `json:"role"`
|
||||
FullName string `json:"full_name"`
|
||||
}
|
||||
|
||||
func NewHandler(queries *sqlc.Queries) *Handler {
|
||||
return &Handler{queries: queries}
|
||||
}
|
||||
|
||||
func (h *Handler) ListUsersByRole(c *fiber.Ctx) error {
|
||||
role := strings.TrimSpace(c.Query("role"))
|
||||
if role == "" {
|
||||
return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "Query parameter 'role' is required")
|
||||
}
|
||||
|
||||
ctx, cancel := shared.WithTimeout()
|
||||
defer cancel()
|
||||
|
||||
users, err := h.queries.ListUsersByRole(ctx, sqlc.UserRole(role))
|
||||
if err != nil {
|
||||
return respond.DatabaseError(c, err)
|
||||
}
|
||||
|
||||
items := make([]UserResponse, 0, len(users))
|
||||
for _, user := range users {
|
||||
items = append(items, mapUser(user, false))
|
||||
}
|
||||
|
||||
return c.JSON(shared.ListResponse[UserResponse]{Data: items})
|
||||
}
|
||||
|
||||
func (h *Handler) GetUserByID(c *fiber.Ctx) error {
|
||||
id, err := params.Int64PathParam(c, "id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := shared.WithTimeout()
|
||||
defer cancel()
|
||||
|
||||
user, err := h.queries.GetUserByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return respond.Error(c, fiber.StatusNotFound, "not_found", "User not found")
|
||||
}
|
||||
return respond.DatabaseError(c, err)
|
||||
}
|
||||
|
||||
return c.JSON(mapUser(user, false))
|
||||
}
|
||||
|
||||
func (h *Handler) CreateUser(c *fiber.Ctx) error {
|
||||
var req createUserRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "Unable to parse request body")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(req.Email) == "" || strings.TrimSpace(req.FullName) == "" || strings.TrimSpace(req.Role) == "" {
|
||||
return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "email, full_name, and role are required")
|
||||
}
|
||||
|
||||
passwordHash, err := shared.MaybeHashPassword(req.Password)
|
||||
if err != nil {
|
||||
return respond.Error(c, fiber.StatusBadRequest, "invalid_request", err.Error())
|
||||
}
|
||||
|
||||
ctx, cancel := shared.WithTimeout()
|
||||
defer cancel()
|
||||
|
||||
user, err := h.queries.CreateUser(ctx, sqlc.CreateUserParams{
|
||||
Email: strings.TrimSpace(req.Email),
|
||||
PasswordHash: passwordHash,
|
||||
Role: sqlc.UserRole(strings.TrimSpace(req.Role)),
|
||||
FullName: strings.TrimSpace(req.FullName),
|
||||
})
|
||||
if err != nil {
|
||||
return respond.DatabaseError(c, err)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(mapUser(user, false))
|
||||
}
|
||||
|
||||
func mapUser(user sqlc.User, includePasswordHash bool) UserResponse {
|
||||
response := UserResponse{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Role: string(user.Role),
|
||||
FullName: user.FullName,
|
||||
IsActive: user.IsActive,
|
||||
CreatedAt: shared.TimePointer(user.CreatedAt),
|
||||
UpdatedAt: shared.TimePointer(user.UpdatedAt),
|
||||
}
|
||||
|
||||
if includePasswordHash {
|
||||
response.PasswordHash = shared.TextPointer(user.PasswordHash)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
13
Backend/internal/handlers/api/users/routes.go
Normal file
13
Backend/internal/handlers/api/users/routes.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
authmw "boostai-backend/internal/middleware"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func RegisterRoutes(app fiber.Router, auth *authmw.AuthMiddleware, h *Handler) {
|
||||
app.Get("/users", auth.RequireTeacher(), h.ListUsersByRole)
|
||||
app.Get("/users/:id", h.GetUserByID)
|
||||
app.Post("/users", auth.RequireTeacher(), h.CreateUser)
|
||||
}
|
||||
Reference in New Issue
Block a user