Files
NewSmoop/server/internal/handlers/users.go

124 lines
3.4 KiB
Go

package handlers
import (
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"smoop-api/internal/crypto"
"smoop-api/internal/dto"
"smoop-api/internal/models"
)
type UsersHandler struct {
db *gorm.DB
}
func NewUsersHandler(db *gorm.DB) *UsersHandler { return &UsersHandler{db: db} }
func (h *UsersHandler) Profile(c *gin.Context) {
claims := MustClaims(c)
uid := ClaimUserID(claims)
var u models.User
if err := h.db.First(&u, uid).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
return
}
c.JSON(http.StatusOK, dto.MapUser(u))
}
func (h *UsersHandler) SetRole(c *gin.Context) {
idStr := c.Param("id")
uid, _ := strconv.Atoi(idStr)
var u models.User
if err := h.db.First(&u, uid).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
return
}
var req dto.UserRoleDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
u.Role = req.Role
if err := h.db.Save(&u).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"})
return
}
c.JSON(http.StatusCreated, dto.MapUser(u))
}
func (h *UsersHandler) List(c *gin.Context) {
var users []models.User
if err := h.db.Order("id asc").Find(&users).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
return
}
out := make([]dto.UserDto, 0, len(users))
for _, u := range users {
out = append(out, dto.MapUser(u))
}
c.JSON(http.StatusOK, out)
}
// POST /users/create (admin) — create user with given role
func (h *UsersHandler) Create(c *gin.Context) {
var req dto.CreateUserDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
role := models.Role(strings.ToLower(req.Role))
if role != models.RoleAdmin && role != models.RoleUser {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role"})
return
}
hash, err := crypto.Hash(req.Password, crypto.DefaultArgon2)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "hash error"})
return
}
u := models.User{Username: req.Username, Password: hash, Role: role}
if err := h.db.Create(&u).Error; err != nil {
// hint duplicate username
e := strings.ToLower(err.Error())
if strings.Contains(e, "duplicate") || strings.Contains(e, "unique") || strings.Contains(e, "exists") {
c.JSON(http.StatusBadRequest, gin.H{"error": "username already exists"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "create failed"})
return
}
c.JSON(http.StatusCreated, dto.MapUser(u))
}
// DELETE /users/:id (admin) — delete user and clear device relations
func (h *UsersHandler) Delete(c *gin.Context) {
idStr := c.Param("id")
id, _ := strconv.Atoi(idStr)
if id <= 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
return
}
var u models.User
if err := h.db.First(&u, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
return
}
if err := h.db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&u).Association("Devices").Clear(); err != nil {
return err
}
return tx.Delete(&u).Error
}); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "delete failed"})
return
}
c.Status(http.StatusNoContent)
}