package main import ( "database/sql" "fmt" "log" "os" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { err := godotenv.Load(".env") if err != nil { log.Println("No .env file found, using system environment variables") } action := "up" if len(os.Args) > 1 { action = os.Args[1] } db, err := connectDB( getEnv("DB_HOST", "127.0.0.1"), getEnv("DB_PORT", "3306"), getEnv("DB_DATABASE", "product_db"), getEnv("DB_USERNAME", "root"), getEnv("DB_PASSWORD", ""), ) if err != nil { log.Fatalf("Could not connect to database: %v", err) } defer db.Close() switch action { case "up": runMigrations(db) case "down": rollbackMigrations(db) case "fresh": rollbackMigrations(db) runMigrations(db) default: log.Fatalf("Unknown command: %s (use: up, down, fresh)", action) } } func connectDB(host, port, database, username, password string) (*sql.DB, error) { dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true", username, password, host, port, ) db, err := sql.Open("mysql", dsn) if err != nil { return nil, err } _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", database)) if err != nil { return nil, fmt.Errorf("failed to create database: %w", err) } _, err = db.Exec(fmt.Sprintf("USE `%s`", database)) if err != nil { return nil, fmt.Errorf("failed to use database: %w", err) } return db, nil } func runMigrations(db *sql.DB) { fmt.Println("Running migrations...") migrations := []struct { name string sql string }{ { name: "001_create_products_table", sql: ` CREATE TABLE IF NOT EXISTS products ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL UNIQUE, description TEXT, price DECIMAL(15, 2) NOT NULL DEFAULT 0.00, stock INT UNSIGNED NOT NULL DEFAULT 0, category VARCHAR(100), sku VARCHAR(100) UNIQUE, is_active TINYINT(1) NOT NULL DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_slug (slug), INDEX idx_category (category), INDEX idx_is_active (is_active), INDEX idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; `, }, } for _, m := range migrations { fmt.Printf(" → Migrating %s... ", m.name) _, err := db.Exec(m.sql) if err != nil { fmt.Printf("FAILED\n Error: %v\n", err) os.Exit(1) } fmt.Println("OK") } fmt.Println("Migration complete!") } func rollbackMigrations(db *sql.DB) { fmt.Println("Rolling back migrations...") tables := []string{"products"} for _, table := range tables { fmt.Printf(" → Dropping table %s... ", table) _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)) if err != nil { fmt.Printf("FAILED\n Error: %v\n", err) os.Exit(1) } fmt.Println("OK") } fmt.Println("Rollback complete!") } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }