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

213 lines
5.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handlers
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"smoop-api/internal/dto"
"smoop-api/internal/models"
)
type TrackersHandler struct {
db *gorm.DB
}
func NewTrackersHandler(db *gorm.DB) *TrackersHandler { return &TrackersHandler{db: db} }
// GET /trackers — list trackers available for user (admin: all)
func (h *TrackersHandler) List(c *gin.Context) {
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
if limit <= 0 || limit > 200 {
limit = 50
}
// Default behavior (fallback if middleware not present): use user context
isFilter := true
var userID uint
if v, ok := c.Get("filterTrackers"); ok {
if b, ok2 := v.(bool); ok2 {
isFilter = b
}
}
if v, ok := c.Get("userID"); ok {
if id, ok2 := v.(uint); ok2 {
userID = id
}
}
// Fallback to claims if middleware wasnt applied
if _, exists := c.Get("filterTrackers"); !exists {
if uc, ok := GetUserContext(c); ok {
if uc.Role == models.RoleAdmin {
isFilter = false
} else {
isFilter = true
userID = uc.ID
}
} else {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
}
var (
total int64
list []models.Tracker
err error
)
if !isFilter {
// Admin: all trackers
err = h.db.Model(&models.Tracker{}).Count(&total).Error
if err == nil {
err = h.db.Preload("Users").Offset(offset).Limit(limit).Find(&list).Error
}
} else {
// Filtered by userID
q := h.db.Model(&models.Tracker{}).
Joins("JOIN user_trackers ut ON ut.tracker_guid = trackers.guid").
Where("ut.user_id = ?", userID)
if err = q.Count(&total).Error; err == nil {
err = h.db.Preload("Users").
Joins("JOIN user_trackers ut ON ut.tracker_guid = trackers.guid").
Where("ut.user_id = ?", userID).
Offset(offset).Limit(limit).
Find(&list).Error
}
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed: " + err.Error()})
return
}
out := make([]dto.TrackerDto, 0, len(list))
for _, t := range list {
out = append(out, dto.MapTracker(t))
}
c.JSON(http.StatusOK, dto.TrackerListDto{
Trackers: out, Offset: offset, Limit: limit, Total: total,
})
}
// POST /trackers/create — create tracker; optional initial user assignments
func (h *TrackersHandler) Create(c *gin.Context) {
var req dto.CreateTrackerDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
t := models.Tracker{GUID: req.GUID, Name: req.Name}
if err := h.db.Create(&t).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "tracker exists?"})
return
}
// optional users
if len(req.UserIDs) > 0 {
users, err := h.fetchUsers(req.UserIDs)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.db.Model(&t).Association("Users").Append(&users); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"})
return
}
}
var withUsers models.Tracker
if err := h.db.Preload("Users").Where("guid = ?", t.GUID).First(&withUsers).Error; err != nil {
c.JSON(http.StatusCreated, dto.TrackerDto{GUID: t.GUID, Name: t.Name})
return
}
c.JSON(http.StatusCreated, dto.MapTracker(withUsers))
}
// POST /trackers/:guid/rename — rename tracker
func (h *TrackersHandler) Rename(c *gin.Context) {
guid := c.Param("guid")
var t models.Tracker
if err := h.db.Where("guid = ?", guid).First(&t).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "tracker not found"})
return
}
var req dto.RenameTrackerDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
t.Name = req.Name
if err := h.db.Save(&t).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"})
return
}
c.JSON(http.StatusCreated, dto.TrackerDto{GUID: t.GUID, Name: t.Name})
}
// POST /trackers/:guid/set_users — replace full user list (admin)
func (h *TrackersHandler) SetUsers(c *gin.Context) {
guid := c.Param("guid")
var t models.Tracker
if err := h.db.Where("guid = ?", guid).First(&t).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "tracker not found"})
return
}
var req dto.SetTrackerUsersDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
users := []models.User{}
if len(req.UserIDs) > 0 {
found, err := h.fetchUsers(req.UserIDs)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
users = found
}
if err := h.db.Model(&t).Association("Users").Clear(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "clear failed"})
return
}
if len(users) > 0 {
if err := h.db.Model(&t).Association("Users").Append(&users); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"})
return
}
}
var withUsers models.Tracker
_ = h.db.Preload("Users").Where("guid = ?", t.GUID).First(&withUsers).Error
c.JSON(http.StatusCreated, dto.MapTracker(withUsers))
}
// local helper (same as devices.go)
func (h *TrackersHandler) fetchUsers(ids []uint) ([]models.User, error) {
unique := make(map[uint]struct{}, len(ids))
clean := make([]uint, 0, len(ids))
for _, id := range ids {
if id != 0 {
if _, ok := unique[id]; !ok {
unique[id] = struct{}{}
clean = append(clean, id)
}
}
}
if len(clean) == 0 {
return nil, nil
}
var users []models.User
if err := h.db.Find(&users, clean).Error; err != nil {
return nil, err
}
if len(users) != len(clean) {
return nil, fmt.Errorf("some users not found")
}
return users, nil
}