2020-02-05 14:51:46 +01:00
|
|
|
package resolvers
|
2020-01-31 17:36:48 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-01-06 17:28:06 +01:00
|
|
|
"os"
|
2020-12-31 00:37:11 +01:00
|
|
|
"path"
|
2021-01-06 17:28:06 +01:00
|
|
|
"strconv"
|
2020-01-31 17:36:48 +01:00
|
|
|
|
2020-12-27 20:07:54 +01:00
|
|
|
api "github.com/photoview/photoview/api/graphql"
|
2020-12-17 22:51:43 +01:00
|
|
|
"github.com/photoview/photoview/api/graphql/auth"
|
|
|
|
"github.com/photoview/photoview/api/graphql/models"
|
2021-05-11 20:40:18 +02:00
|
|
|
"github.com/photoview/photoview/api/graphql/models/actions"
|
2020-12-22 01:14:43 +01:00
|
|
|
"github.com/photoview/photoview/api/scanner"
|
2021-02-27 16:39:04 +01:00
|
|
|
"github.com/photoview/photoview/api/scanner/face_detection"
|
2021-02-15 17:35:28 +01:00
|
|
|
"github.com/photoview/photoview/api/utils"
|
2020-11-28 17:31:19 +01:00
|
|
|
"github.com/pkg/errors"
|
2020-02-22 14:05:33 +01:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2020-11-28 17:31:19 +01:00
|
|
|
"gorm.io/gorm"
|
2020-01-31 17:36:48 +01:00
|
|
|
)
|
|
|
|
|
2020-12-27 20:07:54 +01:00
|
|
|
type userResolver struct {
|
|
|
|
*Resolver
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Resolver) User() api.UserResolver {
|
|
|
|
return &userResolver{r}
|
|
|
|
}
|
|
|
|
|
2021-02-13 15:08:05 +01:00
|
|
|
func (r *queryResolver) User(ctx context.Context, order *models.Ordering, paginate *models.Pagination) ([]*models.User, error) {
|
2020-02-05 14:51:46 +01:00
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
var users []*models.User
|
2020-12-17 21:32:13 +01:00
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := models.FormatSQL(r.DB(ctx).Model(models.User{}), order, paginate).Find(&users).Error; err != nil {
|
2020-02-05 14:51:46 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return users, nil
|
|
|
|
}
|
|
|
|
|
2020-12-27 20:07:54 +01:00
|
|
|
func (r *userResolver) Albums(ctx context.Context, user *models.User) ([]*models.Album, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
user.FillAlbums(r.DB(ctx))
|
2020-12-27 20:07:54 +01:00
|
|
|
|
|
|
|
pointerAlbums := make([]*models.Album, len(user.Albums))
|
|
|
|
for i, album := range user.Albums {
|
|
|
|
pointerAlbums[i] = &album
|
|
|
|
}
|
|
|
|
|
|
|
|
return pointerAlbums, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *userResolver) RootAlbums(ctx context.Context, user *models.User) (albums []*models.Album, err error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
2020-12-27 20:07:54 +01:00
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
err = db.Model(&user).
|
2020-12-27 20:07:54 +01:00
|
|
|
Where("albums.parent_album_id NOT IN (?)",
|
2021-11-11 18:57:02 +01:00
|
|
|
db.Table("user_albums").
|
2020-12-27 20:07:54 +01:00
|
|
|
Select("albums.id").
|
|
|
|
Joins("JOIN albums ON albums.id = user_albums.album_id AND user_albums.user_id = ?", user.ID),
|
2024-07-01 20:04:50 +02:00
|
|
|
).Or("albums.parent_album_id IS NULL").Order("path ASC").
|
2020-12-27 20:07:54 +01:00
|
|
|
Association("Albums").Find(&albums)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-01-31 23:30:34 +01:00
|
|
|
func (r *queryResolver) MyUser(ctx context.Context) (*models.User, error) {
|
|
|
|
|
|
|
|
user := auth.UserFromContext(ctx)
|
|
|
|
if user == nil {
|
|
|
|
return nil, auth.ErrUnauthorized
|
|
|
|
}
|
|
|
|
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
2020-02-01 00:08:23 +01:00
|
|
|
func (r *mutationResolver) AuthorizeUser(ctx context.Context, username string, password string) (*models.AuthorizeResult, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
|
|
|
user, err := models.AuthorizeUser(db, username, password)
|
2020-01-31 17:36:48 +01:00
|
|
|
if err != nil {
|
2020-02-01 00:08:23 +01:00
|
|
|
return &models.AuthorizeResult{
|
2020-01-31 17:36:48 +01:00
|
|
|
Success: false,
|
|
|
|
Status: err.Error(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2020-01-31 23:30:34 +01:00
|
|
|
var token *models.AccessToken
|
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
transactionError := db.Transaction(func(tx *gorm.DB) error {
|
2020-11-28 17:31:19 +01:00
|
|
|
token, err = user.GenerateAccessToken(tx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-31 17:36:48 +01:00
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if transactionError != nil {
|
|
|
|
return nil, transactionError
|
|
|
|
}
|
2020-02-14 14:29:41 +01:00
|
|
|
|
2020-02-01 00:08:23 +01:00
|
|
|
return &models.AuthorizeResult{
|
2020-01-31 17:36:48 +01:00
|
|
|
Success: true,
|
|
|
|
Status: "ok",
|
2020-01-31 18:51:24 +01:00
|
|
|
Token: &token.Value,
|
2020-01-31 17:36:48 +01:00
|
|
|
}, nil
|
|
|
|
}
|
2020-02-05 16:49:51 +01:00
|
|
|
|
|
|
|
func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
|
|
|
siteInfo, err := models.GetSiteInfo(db)
|
2020-02-05 16:49:51 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-02-14 14:29:41 +01:00
|
|
|
if !siteInfo.InitialSetup {
|
|
|
|
return nil, errors.New("not initial setup")
|
|
|
|
}
|
|
|
|
|
2020-12-31 00:37:11 +01:00
|
|
|
rootPath = path.Clean(rootPath)
|
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
var token *models.AccessToken
|
2020-02-05 16:49:51 +01:00
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
transactionError := db.Transaction(func(tx *gorm.DB) error {
|
2020-11-28 17:31:19 +01:00
|
|
|
if err := tx.Exec("UPDATE site_info SET initial_setup = false").Error; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-02-14 14:29:41 +01:00
|
|
|
|
2020-12-20 17:10:00 +01:00
|
|
|
user, err := models.RegisterUser(tx, username, &password, true)
|
2020-11-28 17:31:19 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-12-22 01:14:43 +01:00
|
|
|
_, err = scanner.NewRootAlbum(tx, rootPath, user)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
token, err = user.GenerateAccessToken(tx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if transactionError != nil {
|
2020-02-14 14:29:41 +01:00
|
|
|
return &models.AuthorizeResult{
|
|
|
|
Success: false,
|
|
|
|
Status: err.Error(),
|
|
|
|
}, nil
|
2020-02-05 16:49:51 +01:00
|
|
|
}
|
|
|
|
|
2020-02-14 14:29:41 +01:00
|
|
|
return &models.AuthorizeResult{
|
|
|
|
Success: true,
|
|
|
|
Status: "ok",
|
|
|
|
Token: &token.Value,
|
|
|
|
}, nil
|
2020-02-05 16:49:51 +01:00
|
|
|
}
|
2020-02-16 12:22:00 +01:00
|
|
|
|
2021-04-11 22:31:42 +02:00
|
|
|
func (r *queryResolver) MyUserPreferences(ctx context.Context) (*models.UserPreferences, error) {
|
|
|
|
user := auth.UserFromContext(ctx)
|
|
|
|
if user == nil {
|
|
|
|
return nil, auth.ErrUnauthorized
|
|
|
|
}
|
|
|
|
|
|
|
|
userPref := models.UserPreferences{
|
|
|
|
UserID: user.ID,
|
|
|
|
}
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := r.DB(ctx).Where("user_id = ?", user.ID).FirstOrCreate(&userPref).Error; err != nil {
|
2021-04-11 22:31:42 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &userPref, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *mutationResolver) ChangeUserPreferences(ctx context.Context, language *string) (*models.UserPreferences, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
2021-04-11 22:31:42 +02:00
|
|
|
user := auth.UserFromContext(ctx)
|
|
|
|
if user == nil {
|
|
|
|
return nil, auth.ErrUnauthorized
|
|
|
|
}
|
|
|
|
|
|
|
|
var langTrans *models.LanguageTranslation = nil
|
|
|
|
if language != nil {
|
|
|
|
lng := models.LanguageTranslation(*language)
|
|
|
|
langTrans = &lng
|
|
|
|
}
|
|
|
|
|
|
|
|
var userPref models.UserPreferences
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := db.Where("user_id = ?", user.ID).FirstOrInit(&userPref).Error; err != nil {
|
2021-04-11 22:31:42 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
userPref.UserID = user.ID
|
|
|
|
userPref.Language = langTrans
|
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := db.Save(&userPref).Error; err != nil {
|
2021-04-11 22:31:42 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &userPref, nil
|
|
|
|
}
|
|
|
|
|
2020-02-16 12:22:00 +01:00
|
|
|
// Admin queries
|
2020-12-20 17:10:00 +01:00
|
|
|
func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *string, password *string, admin *bool) (*models.User, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
2020-02-16 12:22:00 +01:00
|
|
|
|
2020-12-20 17:10:00 +01:00
|
|
|
if username == nil && password == nil && admin == nil {
|
2020-11-28 17:31:19 +01:00
|
|
|
return nil, errors.New("no updates requested")
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
var user models.User
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := db.First(&user, id).Error; err != nil {
|
2020-11-28 17:31:19 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-02-16 12:22:00 +01:00
|
|
|
|
|
|
|
if username != nil {
|
2020-11-28 17:31:19 +01:00
|
|
|
user.Username = *username
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
|
|
|
|
2020-02-22 14:05:33 +01:00
|
|
|
if password != nil {
|
|
|
|
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hashedPass := string(hashedPassBytes)
|
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
user.Password = &hashedPass
|
2020-02-22 14:05:33 +01:00
|
|
|
}
|
2020-02-16 12:22:00 +01:00
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
if admin != nil {
|
|
|
|
user.Admin = *admin
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := db.Save(&user).Error; err != nil {
|
2020-11-28 17:31:19 +01:00
|
|
|
return nil, errors.Wrap(err, "failed to update user")
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
return &user, nil
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
|
|
|
|
2020-12-20 17:10:00 +01:00
|
|
|
func (r *mutationResolver) CreateUser(ctx context.Context, username string, password *string, admin bool) (*models.User, error) {
|
2020-02-16 12:22:00 +01:00
|
|
|
|
2020-11-28 17:31:19 +01:00
|
|
|
var user *models.User
|
2020-02-16 12:22:00 +01:00
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
transactionError := r.DB(ctx).Transaction(func(tx *gorm.DB) error {
|
2020-11-28 17:31:19 +01:00
|
|
|
var err error
|
2020-12-20 17:10:00 +01:00
|
|
|
user, err = models.RegisterUser(tx, username, password, admin)
|
2020-11-28 17:31:19 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if transactionError != nil {
|
|
|
|
return nil, transactionError
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *mutationResolver) DeleteUser(ctx context.Context, id int) (*models.User, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
return actions.DeleteUser(r.DB(ctx), id)
|
2020-02-16 12:22:00 +01:00
|
|
|
}
|
2020-12-30 18:36:26 +01:00
|
|
|
|
|
|
|
func (r *mutationResolver) UserAddRootPath(ctx context.Context, id int, rootPath string) (*models.Album, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
2020-12-30 18:36:26 +01:00
|
|
|
|
2020-12-31 00:37:11 +01:00
|
|
|
rootPath = path.Clean(rootPath)
|
|
|
|
|
2020-12-30 18:36:26 +01:00
|
|
|
var user models.User
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := db.First(&user, id).Error; err != nil {
|
2020-12-30 18:36:26 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
newAlbum, err := scanner.NewRootAlbum(db, rootPath, &user)
|
2020-12-30 18:36:26 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return newAlbum, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *mutationResolver) UserRemoveRootAlbum(ctx context.Context, userID int, albumID int) (*models.Album, error) {
|
2021-11-11 18:57:02 +01:00
|
|
|
db := r.DB(ctx)
|
2020-12-30 18:36:26 +01:00
|
|
|
|
|
|
|
var album models.Album
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := db.First(&album, albumID).Error; err != nil {
|
2020-12-30 18:36:26 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-06 17:28:06 +01:00
|
|
|
var deletedAlbumIDs []int = nil
|
|
|
|
|
2021-11-11 18:57:02 +01:00
|
|
|
transactionError := db.Transaction(func(tx *gorm.DB) error {
|
2021-01-06 17:28:06 +01:00
|
|
|
if err := tx.Raw("DELETE FROM user_albums WHERE user_id = ? AND album_id = ?", userID, albumID).Error; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-03 15:55:55 +01:00
|
|
|
children, err := album.GetChildren(tx, nil)
|
2021-01-06 17:28:06 +01:00
|
|
|
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
|
2021-03-16 22:27:27 +01:00
|
|
|
var userAlbumCount int
|
|
|
|
if err := tx.Raw("SELECT COUNT(user_id) FROM user_albums WHERE album_id = ?", albumID).Scan(&userAlbumCount).Error; err != nil {
|
2021-01-06 17:28:06 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-16 22:27:27 +01:00
|
|
|
if userAlbumCount == 0 {
|
2021-01-06 17:28:06 +01:00
|
|
|
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
|
|
|
|
})
|
2020-12-31 00:37:11 +01:00
|
|
|
|
2021-03-16 22:27:27 +01:00
|
|
|
if transactionError != nil {
|
|
|
|
return nil, transactionError
|
2020-12-31 00:37:11 +01:00
|
|
|
}
|
|
|
|
|
2021-01-06 17:28:06 +01:00
|
|
|
if deletedAlbumIDs != nil {
|
|
|
|
// Delete albums from cache
|
|
|
|
for _, id := range deletedAlbumIDs {
|
2021-02-15 17:35:28 +01:00
|
|
|
cacheAlbumPath := path.Join(utils.MediaCachePath(), strconv.Itoa(id))
|
2020-12-31 00:37:11 +01:00
|
|
|
|
2021-01-06 17:28:06 +01:00
|
|
|
if err := os.RemoveAll(cacheAlbumPath); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2021-03-16 22:27:27 +01:00
|
|
|
|
|
|
|
// Reload faces as media might have been deleted
|
2021-11-06 12:23:47 +01:00
|
|
|
if face_detection.GlobalFaceDetector != nil {
|
2021-11-11 18:57:02 +01:00
|
|
|
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(db); err != nil {
|
2021-08-31 11:34:55 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-16 22:27:27 +01:00
|
|
|
}
|
2020-12-30 18:36:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return &album, nil
|
|
|
|
}
|