package sync import ( "regexp" "gorm.io/gorm" "gorm.io/gorm/clause" "radiantwavetech.com/radiantwave/internal/db" "radiantwavetech.com/radiantwave/internal/logger" "radiantwavetech.com/radiantwave/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.radiantwavetech.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 as err for this statement, does not exit the function 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.radiantwavetech.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 } func SyncFrequencies(database *gorm.DB) ([]db.SolfeggioFrequency, error) { fullURL := "https://portal.radiantwavetech.com/api/v1/frequencies" frequencies, err := netreq.GetJSON[db.SolfeggioFrequency](fullURL) if err != nil { logger.Error("Frequency 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.SolfeggioFrequency{}).Error; err != nil { return err } if err := tx.Save(&frequencies).Error; err != nil { return err } return nil }) if err != nil { logger.Error("failed to sync solfeggio frequencies: %v", err) return nil, err } logger.Info("Succesfully synced solfeggio frequencies: %d", len(frequencies)) return frequencies, nil }