package main import ( "flag" "fmt" "log" "time" "git.mchus.pro/mchus/priceforge/internal/appstate" "git.mchus.pro/mchus/priceforge/internal/config" "git.mchus.pro/mchus/priceforge/internal/localdb" "git.mchus.pro/mchus/priceforge/internal/models" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) func main() { configPath := flag.String("config", "config.yaml", "path to config file") defaultLocalDBPath, err := appstate.ResolveDBPath("") if err != nil { log.Fatalf("Failed to resolve default local SQLite path: %v", err) } localDBPath := flag.String("localdb", defaultLocalDBPath, "path to local SQLite database (default: user state dir or QFS_DB_PATH)") dryRun := flag.Bool("dry-run", false, "show what would be migrated without actually doing it") flag.Parse() log.Println("PriceForge Configuration Migration Tool") log.Println("========================================") // Load config for MariaDB connection cfg, err := config.Load(*configPath) if err != nil { log.Fatalf("Failed to load config: %v", err) } // Connect to MariaDB log.Printf("Connecting to MariaDB at %s:%d...", cfg.Database.Host, cfg.Database.Port) mariaDB, err := gorm.Open(mysql.Open(cfg.Database.DSN()), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { log.Fatalf("Failed to connect to MariaDB: %v", err) } log.Println("Connected to MariaDB") // Initialize local SQLite log.Printf("Opening local SQLite at %s...", *localDBPath) local, err := localdb.New(*localDBPath) if err != nil { log.Fatalf("Failed to initialize local database: %v", err) } log.Println("Local SQLite initialized") // Count configurations in MariaDB var serverCount int64 if err := mariaDB.Model(&models.Configuration{}).Count(&serverCount).Error; err != nil { log.Fatalf("Failed to count configurations: %v", err) } log.Printf("Found %d configurations in MariaDB", serverCount) if serverCount == 0 { log.Println("No configurations to migrate") return } // Get all configurations from MariaDB var configs []models.Configuration if err := mariaDB.Find(&configs).Error; err != nil { log.Fatalf("Failed to fetch configurations: %v", err) } // Check existing local configurations localCount := local.CountConfigurations() log.Printf("Found %d configurations in local SQLite", localCount) if *dryRun { log.Println("\n[DRY RUN] Would migrate the following configurations:") for _, c := range configs { userName := c.OwnerUsername if userName == "" { userName = "unknown" } log.Printf(" - %s (UUID: %s, User: %s, Items: %d)", c.Name, c.UUID, userName, len(c.Items)) } log.Printf("\nTotal: %d configurations", len(configs)) return } // Migrate configurations log.Println("\nMigrating configurations...") migrated := 0 skipped := 0 errors := 0 for _, c := range configs { // Check if already exists existing, err := local.GetConfigurationByUUID(c.UUID) if err == nil && existing.ID > 0 { log.Printf(" SKIP: %s (already exists)", c.Name) skipped++ continue } // Convert items localItems := make(localdb.LocalConfigItems, len(c.Items)) for i, item := range c.Items { localItems[i] = localdb.LocalConfigItem{ LotName: item.LotName, Quantity: item.Quantity, UnitPrice: item.UnitPrice, } } // Create local configuration now := time.Now() localConfig := &localdb.LocalConfiguration{ UUID: c.UUID, ServerID: &c.ID, ProjectUUID: c.ProjectUUID, Name: c.Name, Items: localItems, TotalPrice: c.TotalPrice, CustomPrice: c.CustomPrice, Notes: c.Notes, IsTemplate: c.IsTemplate, ServerCount: c.ServerCount, CreatedAt: c.CreatedAt, UpdatedAt: now, SyncedAt: &now, SyncStatus: "synced", OriginalUserID: derefUint(c.UserID), OriginalUsername: c.OwnerUsername, } if err := local.SaveConfiguration(localConfig); err != nil { log.Printf(" ERROR: %s - %v", c.Name, err) errors++ continue } log.Printf(" OK: %s (%d items)", c.Name, len(c.Items)) migrated++ } log.Println("\n========================================") log.Printf("Migration complete!") log.Printf(" Migrated: %d", migrated) log.Printf(" Skipped: %d", skipped) log.Printf(" Errors: %d", errors) // Save connection settings to local SQLite if not exists if !local.HasSettings() { log.Println("\nSaving connection settings to local SQLite...") if err := local.SaveSettings( cfg.Database.Host, cfg.Database.Port, cfg.Database.Name, cfg.Database.User, cfg.Database.Password, ); err != nil { log.Printf("Warning: Failed to save settings: %v", err) } else { log.Println("Connection settings saved") } } fmt.Println("\nDone! You can now run the server with: go run ./cmd/server") } func derefUint(v *uint) uint { if v == nil { return 0 } return *v }