first commit, i i have no idea what i have done

This commit is contained in:
tdv
2025-08-31 22:42:08 +03:00
commit c5632f6a37
177 changed files with 9173 additions and 0 deletions

View File

@@ -0,0 +1,250 @@
package handlers
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"smoop-api/internal/dto"
"smoop-api/internal/models"
)
type DevicesHandler struct {
db *gorm.DB
}
func NewDevicesHandler(db *gorm.DB) *DevicesHandler { return &DevicesHandler{db: db} }
func (h *DevicesHandler) 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
}
var total int64
h.db.Model(&models.Device{}).Count(&total)
var devs []models.Device
if err := h.db.Preload("Users").Offset(offset).Limit(limit).Find(&devs).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
return
}
out := make([]dto.DeviceDto, 0, len(devs))
for _, d := range devs {
out = append(out, dto.MapDevice(d))
}
c.JSON(http.StatusOK, dto.DeviceListDto{Devices: out, Offset: offset, Limit: limit, Total: total})
}
func (h *DevicesHandler) Create(c *gin.Context) {
var req dto.CreateDeviceDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
d := models.Device{GUID: req.GUID, Name: req.Name}
if err := h.db.Create(&d).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "device exists?"})
return
}
// Optional initial user assignments
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(&d).Association("Users").Append(&users); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"})
return
}
}
// Return with users
var withUsers models.Device
if err := h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error; err != nil {
c.JSON(http.StatusCreated, dto.DeviceDto{GUID: d.GUID, Name: d.Name})
return
}
c.JSON(http.StatusCreated, dto.MapDevice(withUsers))
}
func (h *DevicesHandler) Rename(c *gin.Context) {
guid := c.Param("guid")
var d models.Device
if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "device not found"})
return
}
var req dto.RenameDeviceDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
d.Name = req.Name
if err := h.db.Save(&d).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"})
return
}
c.JSON(http.StatusCreated, dto.DeviceDto{GUID: d.GUID, Name: d.Name})
}
func (h *DevicesHandler) AddToUser(c *gin.Context) {
guid := c.Param("guid")
var d models.Device
if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "device not found"})
return
}
var req dto.EditDeviceToUserRelationDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ids := req.UserIDs
if req.UserID != 0 {
ids = append(ids, req.UserID)
}
if len(ids) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "userIds or userId required"})
return
}
users, err := h.fetchUsers(ids)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.db.Model(&d).Association("Users").Append(&users); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"})
return
}
var withUsers models.Device
_ = h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error
c.JSON(http.StatusCreated, dto.MapDevice(withUsers))
}
// SetUsers replaces the users of a device with the provided list.
// Passing an empty list clears all assignments (covers the "no user assigned" case).
func (h *DevicesHandler) SetUsers(c *gin.Context) {
guid := c.Param("guid")
var d models.Device
if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "device not found"})
return
}
var req dto.SetDeviceUsersDto
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Load users (if any)
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
}
// Replace association: Clear() then Append(new)
if err := h.db.Model(&d).Association("Users").Clear(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "clear failed"})
return
}
if len(users) > 0 {
if err := h.db.Model(&d).Association("Users").Append(&users); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"})
return
}
}
var withUsers models.Device
_ = h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error
c.JSON(http.StatusCreated, dto.MapDevice(withUsers))
}
func (h *DevicesHandler) RemoveFromUser(c *gin.Context) {
guid := c.Param("guid")
var d models.Device
if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "device not found"})
return
}
var req dto.RemoveDeviceUsersDto
_ = c.ShouldBindJSON(&req) // ignore error; we support query fallback
ids := make([]uint, 0, len(req.UserIDs)+1)
if req.UserID != 0 {
ids = append(ids, req.UserID)
}
if len(req.UserIDs) > 0 {
ids = append(ids, req.UserIDs...)
}
// query fallback
if len(ids) == 0 {
if q := strings.TrimSpace(c.Query("userId")); q != "" {
if n, err := strconv.Atoi(q); err == nil && n > 0 {
ids = append(ids, uint(n))
}
}
if q := strings.TrimSpace(c.Query("userIds")); q != "" {
for _, p := range strings.Split(q, ",") {
p = strings.TrimSpace(p)
if n, err := strconv.Atoi(p); err == nil && n > 0 {
ids = append(ids, uint(n))
}
}
}
}
if len(ids) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "userIds or userId required"})
return
}
users, err := h.fetchUsers(ids)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if len(users) == 0 {
c.JSON(http.StatusOK, dto.DeviceDto{GUID: d.GUID, Name: d.Name})
return
}
if err := h.db.Model(&d).Association("Users").Delete(&users); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "unlink failed"})
return
}
var withUsers models.Device
_ = h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error
c.JSON(http.StatusOK, dto.MapDevice(withUsers))
}
func (h *DevicesHandler) 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
}