Files
MeBox/internal/database/schema_library_roots.go
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* 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>
2026-09-02 16:26:28 +08:00

112 lines
2.8 KiB
Go

package database
import (
"path/filepath"
"strings"
"gorm.io/gorm"
"github.com/truewhile/MeBox/internal/model"
)
func ensureLibraryRootsCompatibility(db *gorm.DB) error {
var libraries []model.Library
if err := db.Find(&libraries).Error; err != nil {
return err
}
for _, lib := range libraries {
if strings.TrimSpace(lib.Path) == "" {
continue
}
var count int64
if err := db.Model(&model.LibraryRoot{}).Where("library_id = ?", lib.ID).Count(&count).Error; err != nil {
return err
}
if count > 0 {
continue
}
root := model.LibraryRoot{
LibraryID: lib.ID,
Name: firstLibraryRootLabel(lib.Path),
Path: lib.Path,
Enabled: lib.Enabled,
SortOrder: 0,
}
if err := db.Create(&root).Error; err != nil {
return err
}
if err := backfillLibraryRootMedia(db, lib, root); err != nil {
return err
}
}
return nil
}
func backfillLibraryRootMedia(db *gorm.DB, lib model.Library, root model.LibraryRoot) error {
var rows []model.Media
if err := db.Unscoped().
Model(&model.Media{}).
Select("id", "path").
Where("library_id = ? AND (library_root_id = '' OR library_root_id IS NULL)", lib.ID).
Find(&rows).Error; err != nil {
return err
}
rootPath := strings.TrimSpace(root.Path)
for _, row := range rows {
rel, ok := relativePathWithinRoot(row.Path, rootPath)
if !ok {
continue
}
if err := db.Unscoped().Model(&model.Media{}).Where("id = ?", row.ID).Updates(map[string]any{
"library_root_id": root.ID,
"relative_path": rel,
}).Error; err != nil {
return err
}
}
return nil
}
func relativePathWithinRoot(pathValue, root string) (string, bool) {
pathValue = strings.TrimSpace(pathValue)
root = strings.TrimSpace(root)
if pathValue == "" || root == "" {
return "", false
}
if strings.HasPrefix(strings.ToLower(root), "cloud://") || strings.HasPrefix(strings.ToLower(pathValue), "cloud://") {
prefix := strings.TrimRight(root, "/") + "/"
if strings.EqualFold(pathValue, root) {
return "", true
}
if strings.HasPrefix(strings.ToLower(pathValue), strings.ToLower(prefix)) {
return strings.TrimPrefix(pathValue, prefix), true
}
return "", false
}
cleanPath := filepath.Clean(pathValue)
cleanRoot := filepath.Clean(root)
rel, err := filepath.Rel(cleanRoot, cleanPath)
if err != nil || rel == "." || strings.HasPrefix(rel, "..") || filepath.IsAbs(rel) {
return "", false
}
return rel, true
}
func firstLibraryRootLabel(pathValue string) string {
pathValue = strings.TrimSpace(pathValue)
if pathValue == "" {
return ""
}
if strings.HasPrefix(strings.ToLower(pathValue), "cloud://") {
parts := strings.Split(strings.Trim(pathValue, "/"), "/")
if len(parts) > 0 {
return parts[len(parts)-1]
}
}
base := filepath.Base(filepath.Clean(pathValue))
if base == "." || base == string(filepath.Separator) {
return pathValue
}
return base
}