1
Fork 0

Replace database, mostly user related

This commit is contained in:
viktorstrate 2020-11-28 17:31:19 +01:00
parent 22e328b94b
commit d3ebecc3b5
No known key found for this signature in database
GPG Key ID: 3F855605109C1E8A
4 changed files with 199 additions and 246 deletions

View File

@ -2985,9 +2985,9 @@ func (ec *executionContext) _Media_exif(ctx context.Context, field graphql.Colle
if resTmp == nil {
return graphql.Null
}
res := resTmp.(models.MediaEXIF)
res := resTmp.(*models.MediaEXIF)
fc.Result = res
return ec.marshalOMediaEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx, field.Selections, res)
return ec.marshalOMediaEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx, field.Selections, res)
}
func (ec *executionContext) _Media_videoMetadata(ctx context.Context, field graphql.CollectedField, obj *models.Media) (ret graphql.Marshaler) {
@ -9753,8 +9753,11 @@ func (ec *executionContext) marshalOMedia2ᚖgithubᚗcomᚋviktorstrateᚋphoto
return ec._Media(ctx, sel, v)
}
func (ec *executionContext) marshalOMediaEXIF2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx context.Context, sel ast.SelectionSet, v models.MediaEXIF) graphql.Marshaler {
return ec._MediaEXIF(ctx, sel, &v)
func (ec *executionContext) marshalOMediaEXIF2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaEXIF(ctx context.Context, sel ast.SelectionSet, v *models.MediaEXIF) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._MediaEXIF(ctx, sel, v)
}
func (ec *executionContext) marshalOMediaURL2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaURL(ctx context.Context, sel ast.SelectionSet, v *models.MediaURL) graphql.Marshaler {

View File

@ -17,7 +17,7 @@ type Media struct {
AlbumID uint
Album Album
ExifID *uint
Exif MediaEXIF
Exif *MediaEXIF
MediaURL []MediaURL
DateShot time.Time
DateImported time.Time
@ -29,6 +29,10 @@ type Media struct {
SideCarHash *string
}
func (Media) TableName() string {
return "media"
}
type MediaPurpose string
const (

View File

@ -2,14 +2,13 @@ package resolvers
import (
"context"
"database/sql"
"strings"
"github.com/pkg/errors"
api "github.com/viktorstrate/photoview/api/graphql"
"github.com/viktorstrate/photoview/api/graphql/auth"
"github.com/viktorstrate/photoview/api/graphql/models"
"github.com/viktorstrate/photoview/api/scanner"
"gorm.io/gorm"
)
func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*models.Media, error) {
@ -23,18 +22,19 @@ func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*
return nil, err
}
rows, err := r.Database.Query(`
var media []*models.Media
err = r.Database.Raw(`
SELECT media.* FROM media, album
WHERE media.album_id = album.album_id AND album.owner_id = ?
WHERE media.album_id = albums.id AND albums.owner_id = ?
AND media.media_id IN (
SELECT media_id FROM media_url WHERE media_url.media_id = media.media_id
SELECT media_id FROM media_url WHERE media_url.media_id = media.id
)
`+filterSQL, user.UserID)
`+filterSQL, user.ID).Scan(&media).Error
if err != nil {
return nil, err
}
return models.NewMediaFromRows(rows)
return media, nil
}
func (r *queryResolver) Media(ctx context.Context, id int) (*models.Media, error) {
@ -43,21 +43,21 @@ func (r *queryResolver) Media(ctx context.Context, id int) (*models.Media, error
return nil, auth.ErrUnauthorized
}
row := r.Database.QueryRow(`
var media models.Media
err := r.Database.Raw(`
SELECT media.* FROM media
JOIN album ON media.album_id = album.album_id
JOIN albums ON media.album_id = albums.id
WHERE media.media_id = ? AND album.owner_id = ?
AND media.media_id IN (
SELECT media_id FROM media_url WHERE media_url.media_id = media.media_id
)
`, id, user.UserID)
`, id, user.ID).Scan(&media).Error
media, err := models.NewMediaFromRow(row)
if err != nil {
return nil, errors.Wrap(err, "could not get media by media_id and user_id from database")
}
return media, nil
return &media, nil
}
func (r *queryResolver) MediaList(ctx context.Context, ids []int) ([]*models.Media, error) {
@ -70,31 +70,28 @@ func (r *queryResolver) MediaList(ctx context.Context, ids []int) ([]*models.Med
return nil, errors.New("no ids provided")
}
mediaIDQuestions := strings.Repeat("?,", len(ids))[:len(ids)*2-1]
var media []*models.Media
// err := r.Database.
// Select("media.*").
// Joins("Album").
// Where("media.id IN ?", ids).
// Where("album.owner_id = ?", user.ID).
// Where("media.id IN (?)", r.Database.Model(&models.MediaURL{}).Select("media_id").Where("media_url.media_id = media.id")).
// Scan(&media).Error
queryArgs := make([]interface{}, 0)
for _, id := range ids {
queryArgs = append(queryArgs, id)
}
queryArgs = append(queryArgs, user.UserID)
rows, err := r.Database.Query(`
err := r.Database.Raw(`
SELECT media.* FROM media
JOIN album ON media.album_id = album.album_id
WHERE media.media_id IN (`+mediaIDQuestions+`) AND album.owner_id = ?
JOIN albums AS album ON media.album_id = album.id
WHERE media.media_id IN ? AND album.owner_id = ?
AND media.media_id IN (
SELECT media_id FROM media_url WHERE media_url.media_id = media.media_id
)
`, queryArgs...)
`, ids, user.ID).Error
if err != nil {
return nil, errors.Wrap(err, "could not get media list by media_id and user_id from database")
}
media, err := models.NewMediaFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "could not convert database rows to media")
}
return media, nil
}
@ -106,25 +103,20 @@ func (r *Resolver) Media() api.MediaResolver {
return &mediaResolver{r}
}
func (r *mediaResolver) Shares(ctx context.Context, obj *models.Media) ([]*models.ShareToken, error) {
rows, err := r.Database.Query("SELECT * FROM share_token WHERE media_id = ?", obj.MediaID)
if err != nil {
return nil, errors.Wrapf(err, "get shares for media (%s)", obj.Path)
func (r *mediaResolver) Shares(ctx context.Context, media *models.Media) ([]*models.ShareToken, error) {
var shareTokens []*models.ShareToken
if err := r.Database.Where("media_id = ?", media.ID).Find(shareTokens).Error; err != nil {
return nil, errors.Wrapf(err, "get shares for media (%s)", media.Path)
}
return models.NewShareTokensFromRows(rows)
return shareTokens, nil
}
func (r *mediaResolver) Downloads(ctx context.Context, obj *models.Media) ([]*models.MediaDownload, error) {
func (r *mediaResolver) Downloads(ctx context.Context, media *models.Media) ([]*models.MediaDownload, error) {
rows, err := r.Database.Query("SELECT * FROM media_url WHERE media_id = ?", obj.MediaID)
if err != nil {
return nil, errors.Wrapf(err, "get downloads for media (%s)", obj.Path)
}
mediaUrls, err := models.NewMediaURLFromRows(rows)
if err != nil {
return nil, err
var mediaUrls []*models.MediaURL
if err := r.Database.Where("media_id = ?", media.ID).Find(&mediaUrls).Error; err != nil {
return nil, errors.Wrapf(err, "get downloads for media (%s)", media.Path)
}
downloads := make([]*models.MediaDownload, 0)
@ -154,112 +146,110 @@ func (r *mediaResolver) Downloads(ctx context.Context, obj *models.Media) ([]*mo
return downloads, nil
}
func (r *mediaResolver) HighRes(ctx context.Context, obj *models.Media) (*models.MediaURL, error) {
// Try high res first, then
web_types_questions := strings.Repeat("?,", len(scanner.WebMimetypes))[:len(scanner.WebMimetypes)*2-1]
args := make([]interface{}, 0)
args = append(args, obj.MediaID, models.PhotoHighRes, models.MediaOriginal)
for _, webtype := range scanner.WebMimetypes {
args = append(args, webtype)
}
func (r *mediaResolver) HighRes(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
var url models.MediaURL
err := r.Database.
Where("media_url = ?", media.ID).
Where("purpose = ? OR (purpose = ? AND content_type IN ?)", models.PhotoHighRes, models.MediaOriginal, scanner.WebMimetypes).
First(&url).Error
row := r.Database.QueryRow(`
SELECT * FROM media_url WHERE media_id = ? AND
(
purpose = ? OR (purpose = ? AND content_type IN (`+web_types_questions+`))
) LIMIT 1
`, args...)
url, err := models.NewMediaURLFromRow(row)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
} else {
return nil, errors.Wrapf(err, "could not query high-res (%s)", obj.Path)
return nil, errors.Wrapf(err, "could not query high-res (%s)", media.Path)
}
}
return url, nil
return &url, nil
}
func (r *mediaResolver) Thumbnail(ctx context.Context, obj *models.Media) (*models.MediaURL, error) {
row := r.Database.QueryRow("SELECT * FROM media_url WHERE media_id = ? AND (purpose = ? OR purpose = ?)", obj.MediaID, models.PhotoThumbnail, models.VideoThumbnail)
func (r *mediaResolver) Thumbnail(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
var url models.MediaURL
err := r.Database.
Where("media_url = ?", media.ID).
Where("purpose = ? OR purpose = ?", models.PhotoThumbnail, models.VideoThumbnail).
First(&url).Error
url, err := models.NewMediaURLFromRow(row)
if err != nil {
return nil, errors.Wrapf(err, "could not query thumbnail (%s)", obj.Path)
return nil, errors.Wrapf(err, "could not query thumbnail (%s)", media.Path)
}
return url, nil
return &url, nil
}
func (r *mediaResolver) VideoWeb(ctx context.Context, obj *models.Media) (*models.MediaURL, error) {
row := r.Database.QueryRow("SELECT * FROM media_url WHERE media_id = ? AND (purpose = ?)", obj.MediaID, models.VideoWeb)
func (r *mediaResolver) VideoWeb(ctx context.Context, media *models.Media) (*models.MediaURL, error) {
var url models.MediaURL
err := r.Database.
Where("media_url = ?", media.ID).
Where("purpose = ?", models.VideoWeb).
First(&url).Error
url, err := models.NewMediaURLFromRow(row)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
} else {
return nil, errors.Wrapf(err, "could not query video web-format url (%s)", obj.Path)
return nil, errors.Wrapf(err, "could not query video web-format url (%s)", media.Path)
}
}
return url, nil
return &url, nil
}
func (r *mediaResolver) Album(ctx context.Context, obj *models.Media) (*models.Album, error) {
row := r.Database.QueryRow("SELECT album.* from media JOIN album ON media.album_id = album.album_id WHERE media_id = ?", obj.MediaID)
return models.NewAlbumFromRow(row)
}
// func (r *mediaResolver) Album(ctx context.Context, media *models.Media) (*models.Album, error) {
func (r *mediaResolver) Exif(ctx context.Context, obj *models.Media) (*models.MediaEXIF, error) {
row := r.Database.QueryRow("SELECT media_exif.* FROM media NATURAL JOIN media_exif WHERE media.media_id = ?", obj.MediaID)
// r.Database.Model(&media).Joins("Album")
exif, err := models.NewMediaExifFromRow(row)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
} else {
return nil, errors.Wrapf(err, "could not get exif of media from database")
}
}
// row := r.Database.QueryRow("SELECT album.* from media JOIN album ON media.album_id = album.album_id WHERE media_id = ?", media.MediaID)
// return models.NewAlbumFromRow(row)
// }
return exif, nil
}
// func (r *mediaResolver) Exif(ctx context.Context, obj *models.Media) (*models.MediaEXIF, error) {
// row := r.Database.QueryRow("SELECT media_exif.* FROM media NATURAL JOIN media_exif WHERE media.media_id = ?", obj.MediaID)
func (r *mediaResolver) VideoMetadata(ctx context.Context, obj *models.Media) (*models.VideoMetadata, error) {
row := r.Database.QueryRow("SELECT video_metadata.* FROM media JOIN video_metadata ON media.video_metadata_id = video_metadata.metadata_id WHERE media.media_id = ?", obj.MediaID)
// exif, err := models.NewMediaExifFromRow(row)
// if err != nil {
// if errors.Is(err, gorm.ErrRecordNotFound) {
// return nil, nil
// } else {
// return nil, errors.Wrapf(err, "could not get exif of media from database")
// }
// }
metadata, err := models.NewVideoMetadataFromRow(row)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
} else {
return nil, errors.Wrapf(err, "could not get video metadata of media from database")
}
}
// return exif, nil
// }
return metadata, nil
}
// func (r *mediaResolver) VideoMetadata(ctx context.Context, obj *models.Media) (*models.VideoMetadata, error) {
// row := r.Database.QueryRow("SELECT video_metadata.* FROM media JOIN video_metadata ON media.video_metadata_id = video_metadata.metadata_id WHERE media.media_id = ?", obj.MediaID)
// metadata, err := models.NewVideoMetadataFromRow(row)
// if err != nil {
// if errors.Is(err, gorm.ErrRecordNotFound) {
// return nil, nil
// } else {
// return nil, errors.Wrapf(err, "could not get video metadata of media from database")
// }
// }
// return metadata, nil
// }
func (r *mutationResolver) FavoriteMedia(ctx context.Context, mediaID int, favorite bool) (*models.Media, error) {
user := auth.UserFromContext(ctx)
row := r.Database.QueryRow("SELECT media.* FROM media JOIN album ON media.album_id = album.album_id WHERE media.media_id = ? AND album.owner_id = ?", mediaID, user.UserID)
var media models.Media
media, err := models.NewMediaFromRow(row)
if err != nil {
if err := r.Database.Joins("Album").Where("Album.owner_id = ?", user.ID).First(&media, mediaID).Error; err != nil {
return nil, err
}
_, err = r.Database.Exec("UPDATE media SET favorite = ? WHERE media_id = ?", favorite, media.MediaID)
if err != nil {
return nil, errors.Wrap(err, "failed to update media favorite on database")
}
media.Favorite = favorite
return media, nil
if err := r.Database.Save(&media).Error; err != nil {
return nil, errors.Wrap(err, "failed to update media favorite on database")
}
return &media, nil
}

View File

@ -2,12 +2,12 @@ package resolvers
import (
"context"
"errors"
"log"
"github.com/pkg/errors"
"github.com/viktorstrate/photoview/api/graphql/auth"
"github.com/viktorstrate/photoview/api/graphql/models"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// func (r *Resolver) User() UserResolver {
@ -23,13 +23,8 @@ func (r *queryResolver) User(ctx context.Context, filter *models.Filter) ([]*mod
return nil, err
}
rows, err := r.Database.Query("SELECT * FROM user" + filterSQL)
if err != nil {
return nil, err
}
defer rows.Close()
users, err := models.NewUsersFromRows(rows)
var users []*models.User
err = r.Database.Raw("SELECT * FROM users" + filterSQL).Scan(&users).Error
if err != nil {
return nil, err
}
@ -56,20 +51,20 @@ func (r *mutationResolver) AuthorizeUser(ctx context.Context, username string, p
}, nil
}
tx, err := r.Database.Begin()
if err != nil {
return nil, err
}
var token *models.AccessToken
token, err = user.GenerateAccessToken(tx)
if err != nil {
tx.Rollback()
return nil, err
}
transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
token, err = user.GenerateAccessToken(tx)
if err != nil {
return err
}
tx.Commit()
return nil
})
if transactionError != nil {
return nil, transactionError
}
return &models.AuthorizeResult{
Success: true,
@ -78,28 +73,29 @@ func (r *mutationResolver) AuthorizeUser(ctx context.Context, username string, p
}, nil
}
func (r *mutationResolver) RegisterUser(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) {
tx, err := r.Database.Begin()
if err != nil {
return nil, err
}
user, err := models.RegisterUser(tx, username, &password, rootPath, false)
if err != nil {
tx.Rollback()
var token *models.AccessToken
transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
user, err := models.RegisterUser(tx, username, &password, rootPath, false)
if err != nil {
return err
}
token, err = user.GenerateAccessToken(tx)
if err != nil {
tx.Rollback()
return err
}
return nil
})
if transactionError != nil {
return &models.AuthorizeResult{
Success: false,
Status: err.Error(),
}, nil
}
token, err := user.GenerateAccessToken(tx)
if err != nil {
tx.Rollback()
return nil, err
}
if err := tx.Commit(); err != nil {
return nil, err
Status: transactionError.Error(),
}, transactionError
}
return &models.AuthorizeResult{
@ -119,35 +115,33 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
return nil, errors.New("not initial setup")
}
tx, err := r.Database.Begin()
if err != nil {
return nil, err
}
var token *models.AccessToken
if _, err := tx.Exec("UPDATE site_info SET initial_setup = false"); err != nil {
tx.Rollback()
return nil, err
}
transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
if err := tx.Exec("UPDATE site_info SET initial_setup = false").Error; err != nil {
return err
}
user, err := models.RegisterUser(tx, username, &password, rootPath, true)
if err != nil {
tx.Rollback()
user, err := models.RegisterUser(tx, username, &password, rootPath, true)
if err != nil {
return err
}
token, err = user.GenerateAccessToken(tx)
if err != nil {
return err
}
return nil
})
if transactionError != nil {
return &models.AuthorizeResult{
Success: false,
Status: err.Error(),
}, nil
}
token, err := user.GenerateAccessToken(tx)
if err != nil {
tx.Rollback()
return nil, err
}
if err := tx.Commit(); err != nil {
return nil, err
}
return &models.AuthorizeResult{
Success: true,
Status: "ok",
@ -158,34 +152,23 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
// Admin queries
func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *string, rootPath *string, password *string, admin *bool) (*models.User, error) {
user_rows, err := r.Database.Query("SELECT * FROM user WHERE user_id = ?", id)
if err != nil {
if username == nil && rootPath == nil && password == nil && admin == nil {
return nil, errors.New("no updates requested")
}
var user models.User
if err := r.Database.First(&user, id).Error; err != nil {
return nil, err
}
if user_rows.Next() == false {
return nil, errors.New("user not found")
}
user_rows.Close()
update_str := ""
update_args := make([]interface{}, 0)
if username != nil {
update_str += "username = ?, "
update_args = append(update_args, username)
user.Username = *username
}
if rootPath != nil {
if !models.ValidRootPath(*rootPath) {
return nil, errors.New("invalid root path")
}
update_str += "root_path = ?, "
update_args = append(update_args, rootPath)
}
if admin != nil {
update_str += "admin = ?, "
update_args = append(update_args, admin)
if rootPath != nil {
user.RootPath = *rootPath
}
if password != nil {
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
if err != nil {
@ -193,54 +176,36 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *str
}
hashedPass := string(hashedPassBytes)
update_str += "password = ?, "
update_args = append(update_args, hashedPass)
user.Password = &hashedPass
}
if len(update_str) == 0 {
return nil, errors.New("no updates requested")
if admin != nil {
user.Admin = *admin
}
update_str = update_str[:len(update_str)-2]
log.Printf("Updating user with update string: %s\n", update_str)
update_args = append(update_args, id)
res, err := r.Database.Exec("UPDATE user SET "+update_str+" WHERE user_id = ?", update_args...)
if err != nil {
return nil, err
}
rows_aff, err := res.RowsAffected()
if err != nil {
return nil, err
}
if rows_aff == 0 {
return nil, errors.New("no users were updated")
if err := r.Database.Save(&user).Error; err != nil {
return nil, errors.Wrap(err, "failed to update user")
}
row := r.Database.QueryRow("SELECT * FROM user WHERE user_id = ?", id)
user, err := models.NewUserFromRow(row)
if err != nil {
return nil, err
}
return user, nil
return &user, nil
}
func (r *mutationResolver) CreateUser(ctx context.Context, username string, rootPath string, password *string, admin bool) (*models.User, error) {
tx, err := r.Database.Begin()
if err != nil {
return nil, err
}
user, err := models.RegisterUser(tx, username, password, rootPath, admin)
if err != nil {
tx.Rollback()
return nil, err
}
var user *models.User
if err := tx.Commit(); err != nil {
return nil, err
transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
var err error
user, err = models.RegisterUser(tx, username, password, rootPath, admin)
if err != nil {
return err
}
return nil
})
if transactionError != nil {
return nil, transactionError
}
return user, nil
@ -248,24 +213,15 @@ func (r *mutationResolver) CreateUser(ctx context.Context, username string, root
func (r *mutationResolver) DeleteUser(ctx context.Context, id int) (*models.User, error) {
row := r.Database.QueryRow("SELECT * FROM user WHERE user_id = ?", id)
user, err := models.NewUserFromRow(row)
if err != nil {
var user models.User
if err := r.Database.First(&user, id).Error; err != nil {
return nil, err
}
res, err := r.Database.Exec("DELETE FROM user WHERE user_id = ?", id)
if err != nil {
if err := r.Database.Delete(&user).Error; err != nil {
return nil, err
}
rows, err := res.RowsAffected()
if err != nil {
return nil, err
}
if rows == 0 {
return nil, errors.New("no users deleted")
}
return user, nil
return &user, nil
}