Files
radiantwave-legacy/internal/sync/sync.go
T
NeilClack 3cf3e4cf23 Fix database syncronization
When connecting to the internet for the first time, the proclamations table is empty.
If the user navigates to Settings -> Proclamations, then the sync.SyncProclamations
function is called. After this function returns, the ProclamationOptions screen
queries the database for proclamations which returns an empty list. Even though
the database was updated, the ProclamationOptions screen's instance of the database
connection was returning empty.
2026-05-06 10:16:19 -05:00

119 lines
3.2 KiB
Go

package sync
import (
"regexp"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"rhemawavetech.com/rhemawave/internal/db"
"rhemawavetech.com/rhemawave/internal/logger"
"rhemawavetech.com/rhemawave/internal/netreq"
)
var htmlRegex = regexp.MustCompile(`<[^>]*>`)
// SyncProclamations fetches data from the API, cleans it, and updates the local DB.
// It returns the slice of proclamations so the UI can update immediately.
func SyncProclamations(database *gorm.DB) ([]db.Proclamation, error) {
fullURL := "https://portal.rhemawavetech.com/api/v1/proclamations"
logger.Debug("Syncronizing proclamations. URL: %s", fullURL)
proclamations, err := netreq.GetJSON[db.Proclamation](fullURL)
if err != nil {
// If we're offline or API is down, we log it but don't crash.
// The UI can still use whatever is currently in the DB.
logger.Error("Proclamation sync failed, skipping update: %v", err)
return nil, err
}
logger.Debug("Request Granted")
for i := range proclamations {
proclamations[i].Content = htmlRegex.ReplaceAllString(proclamations[i].Content, "")
}
var storedProclamationSettings []db.Proclamation
database.Select("ID", "Selected").Find(&storedProclamationSettings)
// [ID]Selected
stash := make(map[string]bool)
for _, row := range storedProclamationSettings {
stash[row.ID] = row.Selected
}
for i := range proclamations {
proclamations[i].Selected = stash[proclamations[i].ID]
}
err = database.Transaction(func(tx *gorm.DB) error {
ids := make([]string, len(proclamations))
for i, p := range proclamations {
ids[i] = p.ID
}
if len(ids) == 0 {
// API returned nothing → DB should be empty
if err := tx.Where("1 = 1").Delete(&db.Proclamation{}).Error; err != nil {
return err
}
} else {
// Remove anything not in API
if err := tx.Where("id NOT IN ?", ids).Delete(&db.Proclamation{}).Error; err != nil {
return err
}
}
// Upsert (preserve Selected automatically)
if err := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{
"name",
"content",
"solfeggio_frequency_id",
"category_id",
"available",
"price",
}),
}).Create(&proclamations).Error; err != nil {
return err
}
return nil
})
logger.Debug("Proclamations stored to database")
if err != nil {
logger.Error("Failed to save synced proclamations: %v", err)
return nil, err
}
logger.Info("Successfully synced proclamations: %d", len(proclamations))
return proclamations, nil
}
func SyncCategories(database *gorm.DB) ([]db.ProclamationCategory, error) {
fullURL := "https://portal.rhemawavetech.com/api/v1/categories"
categories, err := netreq.GetJSON[db.ProclamationCategory](fullURL)
if err != nil {
logger.Error("Category sync failed, skipping update: %v", err)
return nil, err
}
err = database.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("1 = 1").Delete(&db.ProclamationCategory{}).Error; err != nil {
return err
}
if err := tx.Save(&categories).Error; err != nil {
return err
}
return nil
})
if err != nil {
logger.Error("failed to sync proclamation categories: %v", err)
return nil, err
}
logger.Info("Successfully synced proclamation categories: %d", len(categories))
return categories, nil
}