From d78cef300a753d7804179d586f7f6464f53c72e3 Mon Sep 17 00:00:00 2001 From: viktorstrate Date: Thu, 31 Dec 2020 00:37:11 +0100 Subject: [PATCH] Finish add/remove root paths --- api/graphql/models/album.go | 14 +++++++++ api/graphql/models/base.go | 3 -- api/graphql/resolvers/user.go | 57 +++++++++++++++++++++++++++++++++-- api/scanner/scanner_album.go | 31 ++++++++++++++----- 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/api/graphql/models/album.go b/api/graphql/models/album.go index 0e6149ac..dc8c45f2 100644 --- a/api/graphql/models/album.go +++ b/api/graphql/models/album.go @@ -28,3 +28,17 @@ func (a *Album) BeforeSave(tx *gorm.DB) (err error) { a.PathHash = hex.EncodeToString(hash[:]) return nil } + +func (a *Album) GetChildren(db *gorm.DB) (children []*Album, err error) { + err = db.Raw(` + WITH recursive sub_albums AS ( + SELECT * FROM albums AS root WHERE id = ? + UNION ALL + SELECT child.* FROM albums AS child JOIN sub_albums ON child.parent_album_id = sub_albums.id + ) + + SELECT * FROM sub_albums + `, a.ID).Find(&children).Error + + return children, err +} diff --git a/api/graphql/models/base.go b/api/graphql/models/base.go index b44841df..2c56b385 100644 --- a/api/graphql/models/base.go +++ b/api/graphql/models/base.go @@ -2,8 +2,6 @@ package models import ( "time" - - "gorm.io/gorm" ) type Model struct { @@ -14,5 +12,4 @@ type Model struct { type ModelTimestamps struct { CreatedAt time.Time UpdatedAt time.Time - DeletedAt gorm.DeletedAt `gorm:"index"` } diff --git a/api/graphql/resolvers/user.go b/api/graphql/resolvers/user.go index ceb378eb..03d3fd79 100644 --- a/api/graphql/resolvers/user.go +++ b/api/graphql/resolvers/user.go @@ -2,6 +2,9 @@ package resolvers import ( "context" + "fmt" + "path" + "strings" api "github.com/photoview/photoview/api/graphql" "github.com/photoview/photoview/api/graphql/auth" @@ -106,6 +109,8 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri return nil, errors.New("not initial setup") } + rootPath = path.Clean(rootPath) + var token *models.AccessToken transactionError := r.Database.Transaction(func(tx *gorm.DB) error { @@ -220,12 +225,45 @@ func (r *mutationResolver) DeleteUser(ctx context.Context, id int) (*models.User func (r *mutationResolver) UserAddRootPath(ctx context.Context, id int, rootPath string) (*models.Album, error) { + rootPath = path.Clean(rootPath) + var user models.User if err := r.Database.First(&user, id).Error; err != nil { return nil, err } - // TODO: Check if path exists and that user does not already own rootPath, directly or indirectly + if !models.ValidRootPath(rootPath) { + return nil, errors.New("invalid root path") + } + + upperPaths := make([]string, 1) + upperPath := rootPath + upperPaths[0] = upperPath + for { + + substrIndex := strings.LastIndex(upperPath, "/") + if substrIndex == -1 { + break + } + + if substrIndex == 0 { + upperPaths = append(upperPaths, "/") + break + } + + upperPath = upperPath[0:substrIndex] + upperPaths = append(upperPaths, upperPath) + } + + var upperAlbums []models.Album + if err := r.Database.Model(&user).Association("Albums").Find(&upperAlbums, "albums.path IN (?)", upperPaths); err != nil { + // if err := r.Database.Model(models.Album{}).Where("path IN (?)", upperPaths).Find(&upperAlbums).Error; err != nil { + return nil, err + } + + if len(upperAlbums) > 0 { + return nil, errors.New(fmt.Sprintf("user already owns a path containing this path: %s", upperAlbums[0].Path)) + } newAlbum, err := scanner.NewRootAlbum(r.Database, rootPath, &user) if err != nil { @@ -242,7 +280,22 @@ func (r *mutationResolver) UserRemoveRootAlbum(ctx context.Context, userID int, return nil, err } - result := r.Database.Exec("DELETE FROM user_albums WHERE album_id = ? AND user_id = ?", albumID, userID) + if err := r.Database.Raw("DELETE FROM user_albums WHERE user_id = ? AND album_id = ?", userID, albumID).Error; err != nil { + return nil, err + } + + children, err := album.GetChildren(r.Database) + if err != nil { + return nil, err + } + + childAlbumIDs := make([]int, len(children)) + for i, child := range children { + childAlbumIDs[i] = child.ID + } + + // result := r.Database.Delete(models.Album{}, "id IN (?) OR id = ?", childAlbumIDs, album.ID) + result := r.Database.Exec("DELETE FROM user_albums WHERE user_id = ? and album_id IN (?)", userID, childAlbumIDs) if result.Error != nil { return nil, result.Error } diff --git a/api/scanner/scanner_album.go b/api/scanner/scanner_album.go index 11decfe3..1a328573 100644 --- a/api/scanner/scanner_album.go +++ b/api/scanner/scanner_album.go @@ -19,17 +19,32 @@ func NewRootAlbum(db *gorm.DB, rootPath string, owner *models.User) (*models.Alb *owner, } - album := models.Album{ - Title: path.Base(rootPath), - Path: rootPath, - Owners: owners, - } - - if err := db.Create(&album).Error; err != nil { + var matchedAlbums []models.Album + if err := db.Where("path_hash = MD5(?)", rootPath).Find(&matchedAlbums).Error; err != nil { return nil, err } - return &album, nil + if len(matchedAlbums) > 0 { + album := matchedAlbums[0] + + if err := db.Model(&owner).Association("Albums").Append(&album); err != nil { + return nil, errors.Wrap(err, "failed to add owner to already existing album") + } + + return &album, nil + } else { + album := models.Album{ + Title: path.Base(rootPath), + Path: rootPath, + Owners: owners, + } + + if err := db.Create(&album).Error; err != nil { + return nil, err + } + + return &album, nil + } } func scanAlbum(album *models.Album, cache *AlbumScannerCache, db *gorm.DB) {