PandaWiki/backend/migration/manager.go

81 lines
2.1 KiB
Go

package migration
import (
"fmt"
"time"
"gorm.io/gorm"
"github.com/chaitin/panda-wiki/log"
"github.com/chaitin/panda-wiki/store/pg"
)
type GoMigrationFunc interface {
Execute(tx *gorm.DB) error
}
// MigrationFunc represents a migration function
type MigrationFunc struct {
Name string
Fn func(*gorm.DB) error
}
// Migration represents a migration record in database
type Migration struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"uniqueIndex"`
ExecutedAt time.Time
}
// Manager handles database migrations
type Manager struct {
db *pg.DB
logger *log.Logger
MigrationFunc *MigrationFuncs
}
// NewManager creates a new migration manager
func NewManager(db *pg.DB, logger *log.Logger, migrationFuncs *MigrationFuncs) (*Manager, error) {
return &Manager{
db: db,
logger: logger.WithModule("migration"),
MigrationFunc: migrationFuncs,
}, nil
}
// Execute executes all pending migrations
func (m *Manager) Execute() error {
// Execute pending migrations
for _, migration := range m.MigrationFunc.GetMigrationFuncs() {
m.logger.Info("find migration", log.String("name", migration.Name))
err := m.db.Transaction(func(tx *gorm.DB) error {
// Double check if migration was executed
var record Migration
if err := tx.Where("name = ?", migration.Name).First(&record).Error; err == nil {
// Migration was executed by another instance
m.logger.Info("skip migration", log.String("name", migration.Name))
return nil
}
// Create migration record
if err := tx.Create(&Migration{Name: migration.Name, ExecutedAt: time.Now()}).Error; err != nil {
return fmt.Errorf("create migration record failed: %w", err)
}
m.logger.Info("starting migration", log.String("name", migration.Name))
// Execute the migration
if err := migration.Fn(tx); err != nil {
return fmt.Errorf("execute migration %s failed: %w", migration.Name, err)
}
m.logger.Info("finished migration", log.String("name", migration.Name))
return nil
})
if err != nil {
return err
}
}
return nil
}