package device import ( "errors" "net/http" "strconv" "github.com/gin-gonic/gin" "gorm.io/gorm" "onixbyte.com/pipely/internal/database" "onixbyte.com/pipely/internal/model" ) type Handler struct { db *gorm.DB } func NewHandler(db *gorm.DB) *Handler { return &Handler{db: db} } func (h *Handler) Register(c *gin.Context) { var req model.RegisterDeviceRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } device := &model.Device{ DeviceID: req.DeviceID, IPAddress: req.IPAddress, Hostname: req.Hostname, OS: req.OS, CurrentVersion: req.CurrentVersion, Status: model.DeviceStatusOnline, GroupName: req.GroupName, } if device.GroupName == "" { device.GroupName = "default" } if err := database.UpsertDevice(h.db, device); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, device) } func (h *Handler) Heartbeat(c *gin.Context) { var req model.HeartbeatRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } status := req.Status if status == "" { status = model.DeviceStatusOnline } if err := database.UpdateDeviceHeartbeat(h.db, req.DeviceID, req.CurrentVersion, status); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"status": "acknowledged"}) } func (h *Handler) CheckUpdate(c *gin.Context) { deviceID := c.Query("deviceId") if deviceID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "deviceId is required"}) return } _, artefact, err := database.GetActiveDeploymentForDevice(h.db, deviceID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusOK, model.CheckUpdateResponse{HasUpdate: false}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, model.CheckUpdateResponse{ HasUpdate: true, ArtefactID: artefact.ID, Version: artefact.Version, FileSize: artefact.FileSize, SHA256Hash: artefact.SHA256Hash, DownloadURL: "/api/v1/packages/download/" + strconv.FormatInt(artefact.ID, 10), }) } func (h *Handler) List(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20")) status := c.Query("status") group := c.Query("group") if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } devices, total, err := database.ListDevices(h.db, status, group, page, pageSize) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, model.PaginatedResponse{ Data: devices, Total: total, Page: page, PageSize: pageSize, }) } func (h *Handler) Get(c *gin.Context) { deviceID := c.Param("deviceId") device, err := database.GetDeviceByID(h.db, deviceID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, device) }