mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +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>
112 lines
2.8 KiB
Go
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
|
|
}
|