package database import ( "time" "gorm.io/gorm" "gorm.io/gorm/clause" "onixbyte.com/message-converter-manifest/internal/model" ) // -- Admin Users -- func CreateAdminUser(db *gorm.DB, u *model.AdminUser) error { return db.Create(u).Error } func GetAdminUserByUsername(db *gorm.DB, username string) (*model.AdminUser, error) { var u model.AdminUser err := db.Where("username = ?", username).First(&u).Error if err != nil { return nil, err } return &u, nil } func ListAdminUsers(db *gorm.DB) ([]model.AdminUser, error) { var users []model.AdminUser err := db.Order("created_at").Find(&users).Error return users, err } func DeleteAdminUser(db *gorm.DB, id int64) error { return db.Delete(&model.AdminUser{}, id).Error } func UpdateAdminPassword(db *gorm.DB, id int64, passwordHash string) error { return db.Model(&model.AdminUser{}).Where("id = ?", id). Update("password_hash", passwordHash).Error } // -- Devices -- func UpsertDevice(db *gorm.DB, d *model.Device) error { return db.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "device_id"}}, DoUpdates: clause.AssignmentColumns([]string{"ip_address", "hostname", "os", "current_version", "status", "group_name", "last_heartbeat", "updated_at"}), }).Create(d).Error } func UpdateDeviceHeartbeat(db *gorm.DB, deviceID, version, status string) error { return db.Model(&model.Device{}).Where("device_id = ?", deviceID). Updates(map[string]interface{}{ "current_version": version, "status": status, "last_heartbeat": time.Now(), }).Error } func GetDeviceByID(db *gorm.DB, deviceID string) (*model.Device, error) { var d model.Device err := db.Where("device_id = ?", deviceID).First(&d).Error if err != nil { return nil, err } return &d, nil } func ListDevices(db *gorm.DB, status, group string, page, pageSize int) ([]model.Device, int64, error) { var devices []model.Device var total int64 q := db.Model(&model.Device{}) if status != "" { q = q.Where("status = ?", status) } if group != "" { q = q.Where("group_name = ?", group) } if err := q.Count(&total).Error; err != nil { return nil, 0, err } err := q.Order("last_heartbeat DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&devices).Error return devices, total, err } func GetOfflineDevices(db *gorm.DB, threshold time.Duration) ([]model.Device, error) { var devices []model.Device err := db.Where("last_heartbeat < ? AND status = ?", time.Now().Add(-threshold), model.DeviceStatusOnline). Find(&devices).Error return devices, err } func MarkDevicesOffline(db *gorm.DB, deviceIDs []string) error { return db.Model(&model.Device{}).Where("device_id IN ?", deviceIDs). Update("status", model.DeviceStatusOffline).Error } // -- Artefacts -- func CreateArtefact(db *gorm.DB, a *model.Artefact) error { return db.Create(a).Error } func GetArtefactByID(db *gorm.DB, id int64) (*model.Artefact, error) { var a model.Artefact err := db.First(&a, id).Error if err != nil { return nil, err } return &a, nil } func GetArtefactByVersion(db *gorm.DB, version string) (*model.Artefact, error) { var a model.Artefact err := db.Where("version = ?", version).Order("created_at DESC").First(&a).Error if err != nil { return nil, err } return &a, nil } func ListArtefacts(db *gorm.DB, page, pageSize int) ([]model.Artefact, int64, error) { var artefacts []model.Artefact var total int64 db.Model(&model.Artefact{}).Count(&total) err := db.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&artefacts).Error return artefacts, total, err } // -- Deployments -- func CreateDeployment(db *gorm.DB, d *model.Deployment) error { return db.Create(d).Error } func GetDeploymentByID(db *gorm.DB, id int64) (*model.Deployment, error) { var d model.Deployment err := db.Preload("Artefact").First(&d, id).Error if err != nil { return nil, err } return &d, nil } func ListDeployments(db *gorm.DB, status string, page, pageSize int) ([]model.Deployment, int64, error) { var deployments []model.Deployment var total int64 q := db.Model(&model.Deployment{}) if status != "" { q = q.Where("status = ?", status) } if err := q.Count(&total).Error; err != nil { return nil, 0, err } err := q.Preload("Artefact").Order("created_at DESC"). Offset((page - 1) * pageSize).Limit(pageSize).Find(&deployments).Error return deployments, total, err } func UpdateDeploymentStatus(db *gorm.DB, id int64, status string) error { return db.Model(&model.Deployment{}).Where("id = ?", id).Update("status", status).Error } // -- Deployment Devices -- func CreateDeploymentDevices(db *gorm.DB, deploymentID int64, deviceIDs []string) error { devices := make([]model.DeploymentDevice, len(deviceIDs)) for i, did := range deviceIDs { devices[i] = model.DeploymentDevice{DeploymentID: deploymentID, DeviceID: did, Status: model.DeployDeviceStatusPending} } return db.Clauses(clause.OnConflict{DoNothing: true}).Create(&devices).Error } func GetActiveDeploymentForDevice(db *gorm.DB, deviceID string) (*model.Deployment, *model.Artefact, error) { var dd model.DeploymentDevice err := db.Where("device_id = ? AND status = ?", deviceID, model.DeployDeviceStatusPending). Preload("Deployment.Artefact").First(&dd).Error if err != nil { return nil, nil, err } var d model.Deployment db.Preload("Artefact").First(&d, dd.DeploymentID) if d.Status != model.DeploymentStatusActive { return nil, nil, gorm.ErrRecordNotFound } return &d, d.Artefact, nil } func UpdateDeploymentDeviceStatus(db *gorm.DB, deploymentID int64, deviceID, status, errMsg string) error { updates := map[string]interface{}{"status": status, "error": errMsg} now := time.Now() if status == model.DeployDeviceStatusDownloaded { updates["downloaded_at"] = &now } if status == model.DeployDeviceStatusCompleted || status == model.DeployDeviceStatusFailed { updates["completed_at"] = &now } return db.Model(&model.DeploymentDevice{}). Where("deployment_id = ? AND device_id = ?", deploymentID, deviceID). Updates(updates).Error } func GetDeploymentDeviceStats(db *gorm.DB, deploymentID int64) (map[string]int64, error) { type row struct { Status string Count int64 } var rows []row if err := db.Model(&model.DeploymentDevice{}). Select("status, COUNT(*) as count"). Where("deployment_id = ?", deploymentID). Group("status").Scan(&rows).Error; err != nil { return nil, err } stats := map[string]int64{} for _, r := range rows { stats[r.Status] = r.Count } return stats, nil } func GetDeploymentDevices(db *gorm.DB, deploymentID int64, status string, page, pageSize int) ([]model.DeploymentDevice, int64, error) { var devices []model.DeploymentDevice var total int64 q := db.Model(&model.DeploymentDevice{}).Where("deployment_id = ?", deploymentID) if status != "" { q = q.Where("status = ?", status) } q.Count(&total) err := q.Order("created_at").Offset((page - 1) * pageSize).Limit(pageSize).Find(&devices).Error return devices, total, err }