Properly clean up when a user <-> album relation is deleted
This commit is contained in:
parent
59048c8416
commit
a1a14286d6
|
@ -11,7 +11,7 @@ type Album struct {
|
|||
Model
|
||||
Title string `gorm:"not null"`
|
||||
ParentAlbumID *int
|
||||
ParentAlbum *Album
|
||||
ParentAlbum *Album `gorm:"constraint:OnDelete:SET NULL;"`
|
||||
// OwnerID int `gorm:"not null"`
|
||||
// Owner User
|
||||
Owners []User `gorm:"many2many:user_albums"`
|
||||
|
|
|
@ -17,16 +17,16 @@ type Media struct {
|
|||
Path string `gorm:"not null"`
|
||||
PathHash string `gorm:"not null"`
|
||||
AlbumID int `gorm:"not null"`
|
||||
Album Album
|
||||
Album Album `gorm:"constraint:OnDelete:CASCADE;"`
|
||||
ExifID *int
|
||||
Exif *MediaEXIF
|
||||
MediaURL []MediaURL
|
||||
DateShot time.Time `gorm:"not null"`
|
||||
DateImported time.Time `gorm:"not null"`
|
||||
Exif *MediaEXIF `gorm:"constraint:OnDelete:SET NULL;"`
|
||||
MediaURL []MediaURL `gorm:"constraint:OnDelete:CASCADE;"`
|
||||
DateShot time.Time `gorm:"not null"`
|
||||
DateImported time.Time `gorm:"not null"`
|
||||
// Favorite bool `gorm:"not null, default:false"`
|
||||
Type MediaType `gorm:"not null"`
|
||||
VideoMetadataID *int
|
||||
VideoMetadata *VideoMetadata
|
||||
VideoMetadata *VideoMetadata `gorm:"constraint:OnDelete:SET NULL;"`
|
||||
SideCarPath *string
|
||||
SideCarHash *string
|
||||
|
||||
|
@ -52,6 +52,18 @@ func (m *Media) BeforeSave(tx *gorm.DB) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Media) BeforeDelete(tx *gorm.DB) error {
|
||||
if err := tx.Model(m).Association("Exif").Clear(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Model(m).Association("MediaURL").Clear(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type MediaPurpose string
|
||||
|
||||
const (
|
||||
|
@ -64,8 +76,8 @@ const (
|
|||
|
||||
type MediaURL struct {
|
||||
Model
|
||||
MediaID int `gorm:"not null"`
|
||||
Media Media
|
||||
MediaID int `gorm:"not null"`
|
||||
Media Media `gorm:"constraint:OnDelete:CASCADE;"`
|
||||
MediaName string `gorm:"not null"`
|
||||
Width int `gorm:"not null"`
|
||||
Height int `gorm:"not null"`
|
||||
|
|
|
@ -8,13 +8,13 @@ type ShareToken struct {
|
|||
Model
|
||||
Value string `gorm:"not null"`
|
||||
OwnerID int `gorm:"not null"`
|
||||
Owner User
|
||||
Owner User `gorm:"constraint:OnDelete:CASCADE;"`
|
||||
Expire *time.Time
|
||||
Password *string
|
||||
AlbumID *int
|
||||
Album *Album
|
||||
Album *Album `gorm:"constraint:OnDelete:CASCADE;"`
|
||||
MediaID *int
|
||||
Media *Media
|
||||
Media *Media `gorm:"constraint:OnDelete:CASCADE;"`
|
||||
}
|
||||
|
||||
func (share *ShareToken) Token() string {
|
||||
|
|
|
@ -3,7 +3,9 @@ package resolvers
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
api "github.com/photoview/photoview/api/graphql"
|
||||
|
@ -280,28 +282,65 @@ func (r *mutationResolver) UserRemoveRootAlbum(ctx context.Context, userID int,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.Database.Raw("DELETE FROM user_albums WHERE user_id = ? AND album_id = ?", userID, albumID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var deletedAlbumIDs []int = nil
|
||||
|
||||
err := r.Database.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Raw("DELETE FROM user_albums WHERE user_id = ? AND album_id = ?", userID, albumID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
children, err := album.GetChildren(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
childAlbumIDs := make([]int, len(children))
|
||||
for i, child := range children {
|
||||
childAlbumIDs[i] = child.ID
|
||||
}
|
||||
|
||||
result := tx.Exec("DELETE FROM user_albums WHERE user_id = ? and album_id IN (?)", userID, childAlbumIDs)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return errors.New("No relation deleted")
|
||||
}
|
||||
|
||||
// Cleanup if no user owns the album anymore
|
||||
var count int
|
||||
if err := tx.Raw("SELECT COUNT(user_id) FROM user_albums WHERE album_id = ?", albumID).Scan(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
deletedAlbumIDs = append(childAlbumIDs, albumID)
|
||||
childAlbumIDs = nil
|
||||
|
||||
// Delete albums from database
|
||||
if err := tx.Delete(&models.Album{}, "id IN (?)", deletedAlbumIDs).Error; err != nil {
|
||||
deletedAlbumIDs = nil
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
if deletedAlbumIDs != nil {
|
||||
// Delete albums from cache
|
||||
for _, id := range deletedAlbumIDs {
|
||||
cacheAlbumPath := path.Join(scanner.PhotoCache(), strconv.Itoa(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
|
||||
}
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, errors.New("No relation deleted")
|
||||
if err := os.RemoveAll(cacheAlbumPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &album, nil
|
||||
|
|
|
@ -63,7 +63,6 @@ const EditUserRow = ({
|
|||
variables: {
|
||||
id: user.id,
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -93,6 +93,7 @@ const EditNewRootPath = ({ userID }) => {
|
|||
icon: 'add',
|
||||
content: 'Add',
|
||||
onClick: () => {
|
||||
setValue('')
|
||||
addRootPath({
|
||||
variables: {
|
||||
id: userID,
|
||||
|
|
|
@ -5,21 +5,10 @@ import EditUserRow from './EditUserRow'
|
|||
import ViewUserRow from './ViewUserRow'
|
||||
|
||||
const updateUserMutation = gql`
|
||||
mutation updateUser(
|
||||
$id: ID!
|
||||
$username: String
|
||||
$rootPath: String
|
||||
$admin: Boolean
|
||||
) {
|
||||
updateUser(
|
||||
id: $id
|
||||
username: $username
|
||||
rootPath: $rootPath
|
||||
admin: $admin
|
||||
) {
|
||||
mutation updateUser($id: ID!, $username: String, $admin: Boolean) {
|
||||
updateUser(id: $id, username: $username, admin: $admin) {
|
||||
id
|
||||
username
|
||||
rootPath
|
||||
admin
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue