159 lines
3.7 KiB
Go
159 lines
3.7 KiB
Go
|
package migrate
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/jinzhu/gorm"
|
||
|
|
||
|
"github.com/photoprism/photoprism/pkg/clean"
|
||
|
"github.com/photoprism/photoprism/pkg/txt"
|
||
|
)
|
||
|
|
||
|
var versionOnce = sync.Once{}
|
||
|
var versionMutex = sync.Mutex{}
|
||
|
|
||
|
// Version represents the application version.
|
||
|
type Version struct {
|
||
|
ID uint `gorm:"primary_key" yaml:"-"`
|
||
|
Version string `gorm:"size:255;unique_index:idx_version_edition;" json:"Version" yaml:"Version,omitempty"`
|
||
|
Edition string `gorm:"size:255;unique_index:idx_version_edition;" json:"Edition" yaml:"Edition,omitempty"`
|
||
|
Error string `gorm:"size:255;" json:"Error" yaml:"Error,omitempty"`
|
||
|
CreatedAt time.Time `yaml:"CreatedAt,omitempty"`
|
||
|
UpdatedAt time.Time `yaml:"UpdatedAt,omitempty"`
|
||
|
MigratedAt *time.Time `json:"MigratedAt" yaml:"MigratedAt,omitempty"`
|
||
|
}
|
||
|
|
||
|
// TableName returns the entity database table name.
|
||
|
func (Version) TableName() string {
|
||
|
return "versions"
|
||
|
}
|
||
|
|
||
|
var UnknownVersion = Version{
|
||
|
Version: "0.0.0",
|
||
|
Edition: "dev",
|
||
|
}
|
||
|
|
||
|
// NeedsMigration tests if the Version is not yet installed.
|
||
|
func (m *Version) NeedsMigration() bool {
|
||
|
if m.MigratedAt == nil {
|
||
|
return true
|
||
|
} else if m.Unknown() {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
return m.MigratedAt.IsZero()
|
||
|
}
|
||
|
|
||
|
// Migrated flags the version as installed and migrated.
|
||
|
func (m *Version) Migrated(db *gorm.DB) error {
|
||
|
if err := m.CreateTable(db); err != nil {
|
||
|
return err
|
||
|
} else if m.Unknown() {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
timeStamp := time.Now().UTC().Truncate(time.Second)
|
||
|
m.MigratedAt = &timeStamp
|
||
|
m.Error = ""
|
||
|
|
||
|
return db.Model(m).Updates(Values{"MigratedAt": m.MigratedAt, "Error": m.Error}).Error
|
||
|
}
|
||
|
|
||
|
// NewVersion creates a Version entity from a model name and a make name.
|
||
|
func NewVersion(version, edition string) *Version {
|
||
|
result := &Version{
|
||
|
Version: txt.Clip(version, txt.ClipLongName),
|
||
|
Edition: txt.Clip(edition, txt.ClipLongName),
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// Create inserts a new row to the database.
|
||
|
func (m *Version) Create(db *gorm.DB) error {
|
||
|
if err := m.CreateTable(db); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
versionMutex.Lock()
|
||
|
defer versionMutex.Unlock()
|
||
|
|
||
|
return db.Create(m).Error
|
||
|
}
|
||
|
|
||
|
// Save saved the record in the database.
|
||
|
func (m *Version) Save(db *gorm.DB) error {
|
||
|
if err := m.CreateTable(db); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
versionMutex.Lock()
|
||
|
defer versionMutex.Unlock()
|
||
|
|
||
|
return db.Save(m).Error
|
||
|
}
|
||
|
|
||
|
// Find fetches an existing record from the database.
|
||
|
func (m *Version) Find(db *gorm.DB) *Version {
|
||
|
if err := m.CreateTable(db); err != nil {
|
||
|
return nil
|
||
|
} else if m.Version == "" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
result := Version{}
|
||
|
|
||
|
if err := db.Where("version = ? AND edition = ?", m.Version, m.Edition).First(&result).Error; err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return &result
|
||
|
}
|
||
|
|
||
|
// FirstOrCreateVersion returns the existing row, inserts a new row or nil in case of errors.
|
||
|
func FirstOrCreateVersion(db *gorm.DB, m *Version) *Version {
|
||
|
if m == nil {
|
||
|
return &UnknownVersion
|
||
|
} else if err := m.CreateTable(db); err != nil {
|
||
|
return &UnknownVersion
|
||
|
} else if m.Version == "" {
|
||
|
return &UnknownVersion
|
||
|
}
|
||
|
|
||
|
// Find existing version or create new record.
|
||
|
if found := m.Find(db); found != nil {
|
||
|
return found
|
||
|
} else if err := m.Create(db); err != nil {
|
||
|
log.Errorf("version: %s (create)", err)
|
||
|
} else {
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
return &UnknownVersion
|
||
|
}
|
||
|
|
||
|
// String returns an identifier that can be used in logs.
|
||
|
func (m *Version) String() string {
|
||
|
return clean.Log(m.Version + "-" + m.Edition)
|
||
|
}
|
||
|
|
||
|
// Unknown checks if the version is unknown.
|
||
|
func (m *Version) Unknown() bool {
|
||
|
return m.Version == UnknownVersion.Version
|
||
|
}
|
||
|
|
||
|
// CreateTable creates the versions database table if needed.
|
||
|
func (m *Version) CreateTable(db *gorm.DB) (err error) {
|
||
|
if db == nil {
|
||
|
return fmt.Errorf("db is nil")
|
||
|
}
|
||
|
|
||
|
versionOnce.Do(func() {
|
||
|
err = db.AutoMigrate(&Version{}).Error
|
||
|
})
|
||
|
|
||
|
return err
|
||
|
}
|