mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 03:06:38 +08:00
b0fe40142a
* Rebrand MMTL to MeBox across codebase and assets Rename the project display name, Go module path, environment variable prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl to MeBox/mebox. Replace logo assets with the new MeBox icon and keep legacy SQLite migration support for existing mmtl.db deployments. Co-authored-by: truewhile <truewhile@users.noreply.github.com> * Fix logo icons: use cube-only crop without truncated text Previous icon generation cropped too much of the source image, including partial MeBox wordmark text that was cut off in square icon containers. Regenerate logo-64/192/512, favicon, and SVG from cube-only region. Co-authored-by: truewhile <truewhile@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
|
|
"github.com/truewhile/MeBox/internal/model"
|
|
)
|
|
|
|
func resetBootstrapTargetBeforeSQLiteMigrationIfSafe(src, target *gorm.DB, log *zap.Logger) error {
|
|
hasRows, err := sqliteSourceHasMigratableRows(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !hasRows {
|
|
return nil
|
|
}
|
|
bootstrapOnly, err := targetLooksLikeBootstrapOnly(target)
|
|
if err != nil || !bootstrapOnly {
|
|
return err
|
|
}
|
|
for i := len(model.AllModels()) - 1; i >= 0; i-- {
|
|
m := model.AllModels()[i]
|
|
if !target.Migrator().HasTable(m) {
|
|
continue
|
|
}
|
|
if err := target.Session(&gorm.Session{AllowGlobalUpdate: true}).Unscoped().Delete(m).Error; err != nil {
|
|
return fmt.Errorf("clear bootstrap target table %T: %w", m, err)
|
|
}
|
|
}
|
|
if log != nil {
|
|
log.Warn("cleared bootstrap postgres rows before sqlite migration")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func sqliteSourceHasMigratableRows(src *gorm.DB) (bool, error) {
|
|
for _, table := range []string{"users", "libraries", "media", "settings"} {
|
|
exists, err := sqliteTableExists(src, table)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !exists {
|
|
continue
|
|
}
|
|
var count int64
|
|
if err := src.Raw("SELECT COUNT(1) FROM " + quoteIdent(table)).Scan(&count).Error; err != nil {
|
|
return false, fmt.Errorf("count sqlite table %s: %w", table, err)
|
|
}
|
|
if count > 0 {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func targetLooksLikeBootstrapOnly(target *gorm.DB) (bool, error) {
|
|
for _, m := range []any{
|
|
&model.Library{},
|
|
&model.Series{},
|
|
&model.Media{},
|
|
&model.PlaybackHistory{},
|
|
&model.Favorite{},
|
|
&model.Playlist{},
|
|
&model.PlaylistItem{},
|
|
} {
|
|
if !target.Migrator().HasTable(m) {
|
|
continue
|
|
}
|
|
var count int64
|
|
if err := target.Unscoped().Model(m).Count(&count).Error; err != nil {
|
|
return false, err
|
|
}
|
|
if count > 0 {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
var userCount int64
|
|
if !target.Migrator().HasTable(&model.User{}) {
|
|
return true, nil
|
|
}
|
|
if err := target.Model(&model.User{}).Count(&userCount).Error; err != nil {
|
|
return false, err
|
|
}
|
|
if userCount == 0 {
|
|
return true, nil
|
|
}
|
|
if userCount != 1 {
|
|
return false, nil
|
|
}
|
|
var user model.User
|
|
if err := target.Unscoped().Where("username = ?", "admin").First(&user).Error; err != nil {
|
|
return false, nil
|
|
}
|
|
return user.Role == "admin", nil
|
|
}
|
|
|
|
func sqliteMigrationMarkedComplete(db *gorm.DB) (bool, error) {
|
|
var value string
|
|
err := db.Raw("SELECT value FROM "+quoteIdent("settings")+" WHERE "+quoteIdent("key")+" = ?", sqliteMigrationCompleteSettingKey).Scan(&value).Error
|
|
if err != nil {
|
|
return false, fmt.Errorf("check sqlite migration marker: %w", err)
|
|
}
|
|
return strings.EqualFold(strings.TrimSpace(value), "true"), nil
|
|
}
|
|
|
|
func markSQLiteMigrationComplete(db *gorm.DB) error {
|
|
now := time.Now()
|
|
if err := db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "key"}},
|
|
DoUpdates: clause.AssignmentColumns([]string{"value", "updated_at"}),
|
|
}).Create(&model.Setting{
|
|
Key: sqliteMigrationCompleteSettingKey,
|
|
Value: "true",
|
|
UpdatedAt: now,
|
|
}).Error; err != nil {
|
|
return fmt.Errorf("mark sqlite migration complete: %w", err)
|
|
}
|
|
return nil
|
|
}
|