Files
pipely/internal/database/repository.go
T
siujamo 033082954a refactor: rename module to pipely and migrate web UI to MUI
- Rename Go module from onixbyte.com/message-converter-manifest to onixbyte.com/pipely
- Replace shadcn UI with Material UI components across all pages
- Add axios-based API client in shared/web-client with credentials management
- Organise API calls into feature modules: auth, devices, deployments, artefacts, users
- Add .env.example with VITE_API_BASE_URL pointing to pipely.onixbyte.com
- Fix deprecated FormEvent → MouseEvent in dialog submit handlers
2026-07-07 14:40:09 +08:00

243 lines
6.8 KiB
Go

package database
import (
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"onixbyte.com/pipely/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
}