restructure user <-> albums relations in database

This commit is contained in:
viktorstrate
2020-12-20 17:10:00 +01:00
parent ae8e764539
commit 732ccd05ab
9 changed files with 193 additions and 308 deletions

View File

@@ -60,6 +60,7 @@ func MigrateDatabase(db *gorm.DB) error {
&models.MediaEXIF{}, &models.MediaEXIF{},
&models.VideoMetadata{}, &models.VideoMetadata{},
&models.ShareToken{}, &models.ShareToken{},
&models.UserMediaData{},
) )
return nil return nil

View File

@@ -9,6 +9,7 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -64,16 +65,19 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns= github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns=
@@ -103,6 +107,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM=
golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -115,20 +115,19 @@ type ComplexityRoot struct {
Mutation struct { Mutation struct {
AuthorizeUser func(childComplexity int, username string, password string) int AuthorizeUser func(childComplexity int, username string, password string) int
CreateUser func(childComplexity int, username string, rootPath string, password *string, admin bool) int CreateUser func(childComplexity int, username string, password *string, admin bool) int
DeleteShareToken func(childComplexity int, token string) int DeleteShareToken func(childComplexity int, token string) int
DeleteUser func(childComplexity int, id int) int DeleteUser func(childComplexity int, id int) int
FavoriteMedia func(childComplexity int, mediaID int, favorite bool) int FavoriteMedia func(childComplexity int, mediaID int, favorite bool) int
InitialSetupWizard func(childComplexity int, username string, password string, rootPath string) int InitialSetupWizard func(childComplexity int, username string, password string, rootPath string) int
ProtectShareToken func(childComplexity int, token string, password *string) int ProtectShareToken func(childComplexity int, token string, password *string) int
RegisterUser func(childComplexity int, username string, password string, rootPath string) int
ScanAll func(childComplexity int) int ScanAll func(childComplexity int) int
ScanUser func(childComplexity int, userID int) int ScanUser func(childComplexity int, userID int) int
SetPeriodicScanInterval func(childComplexity int, interval int) int SetPeriodicScanInterval func(childComplexity int, interval int) int
SetScannerConcurrentWorkers func(childComplexity int, workers int) int SetScannerConcurrentWorkers func(childComplexity int, workers int) int
ShareAlbum func(childComplexity int, albumID int, expire *time.Time, password *string) int ShareAlbum func(childComplexity int, albumID int, expire *time.Time, password *string) int
ShareMedia func(childComplexity int, mediaID int, expire *time.Time, password *string) int ShareMedia func(childComplexity int, mediaID int, expire *time.Time, password *string) int
UpdateUser func(childComplexity int, id int, username *string, rootPath *string, password *string, admin *bool) int UpdateUser func(childComplexity int, id int, username *string, password *string, admin *bool) int
} }
Notification struct { Notification struct {
@@ -193,8 +192,8 @@ type ComplexityRoot struct {
User struct { User struct {
Admin func(childComplexity int) int Admin func(childComplexity int) int
Albums func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
RootPath func(childComplexity int) int
Username func(childComplexity int) int Username func(childComplexity int) int
} }
@@ -225,12 +224,13 @@ type MediaResolver interface {
HighRes(ctx context.Context, obj *models.Media) (*models.MediaURL, error) HighRes(ctx context.Context, obj *models.Media) (*models.MediaURL, error)
VideoWeb(ctx context.Context, obj *models.Media) (*models.MediaURL, error) VideoWeb(ctx context.Context, obj *models.Media) (*models.MediaURL, error)
Favorite(ctx context.Context, obj *models.Media) (bool, error)
Shares(ctx context.Context, obj *models.Media) ([]*models.ShareToken, error) Shares(ctx context.Context, obj *models.Media) ([]*models.ShareToken, error)
Downloads(ctx context.Context, obj *models.Media) ([]*models.MediaDownload, error) Downloads(ctx context.Context, obj *models.Media) ([]*models.MediaDownload, error)
} }
type MutationResolver interface { type MutationResolver interface {
AuthorizeUser(ctx context.Context, username string, password string) (*models.AuthorizeResult, error) AuthorizeUser(ctx context.Context, username string, password string) (*models.AuthorizeResult, error)
RegisterUser(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error)
InitialSetupWizard(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) InitialSetupWizard(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error)
ScanAll(ctx context.Context) (*models.ScannerResult, error) ScanAll(ctx context.Context) (*models.ScannerResult, error)
ScanUser(ctx context.Context, userID int) (*models.ScannerResult, error) ScanUser(ctx context.Context, userID int) (*models.ScannerResult, error)
@@ -239,8 +239,8 @@ type MutationResolver interface {
DeleteShareToken(ctx context.Context, token string) (*models.ShareToken, error) DeleteShareToken(ctx context.Context, token string) (*models.ShareToken, error)
ProtectShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error) ProtectShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error)
FavoriteMedia(ctx context.Context, mediaID int, favorite bool) (*models.Media, error) FavoriteMedia(ctx context.Context, mediaID int, favorite bool) (*models.Media, error)
UpdateUser(ctx context.Context, id int, username *string, rootPath *string, password *string, admin *bool) (*models.User, error) UpdateUser(ctx context.Context, id int, username *string, password *string, admin *bool) (*models.User, error)
CreateUser(ctx context.Context, username string, rootPath string, password *string, admin bool) (*models.User, error) CreateUser(ctx context.Context, username string, password *string, admin bool) (*models.User, error)
DeleteUser(ctx context.Context, id int) (*models.User, error) DeleteUser(ctx context.Context, id int) (*models.User, error)
SetPeriodicScanInterval(ctx context.Context, interval int) (int, error) SetPeriodicScanInterval(ctx context.Context, interval int) (int, error)
SetScannerConcurrentWorkers(ctx context.Context, workers int) (int, error) SetScannerConcurrentWorkers(ctx context.Context, workers int) (int, error)
@@ -622,7 +622,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false return 0, false
} }
return e.complexity.Mutation.CreateUser(childComplexity, args["username"].(string), args["rootPath"].(string), args["password"].(*string), args["admin"].(bool)), true return e.complexity.Mutation.CreateUser(childComplexity, args["username"].(string), args["password"].(*string), args["admin"].(bool)), true
case "Mutation.deleteShareToken": case "Mutation.deleteShareToken":
if e.complexity.Mutation.DeleteShareToken == nil { if e.complexity.Mutation.DeleteShareToken == nil {
@@ -684,18 +684,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.ProtectShareToken(childComplexity, args["token"].(string), args["password"].(*string)), true return e.complexity.Mutation.ProtectShareToken(childComplexity, args["token"].(string), args["password"].(*string)), true
case "Mutation.registerUser":
if e.complexity.Mutation.RegisterUser == nil {
break
}
args, err := ec.field_Mutation_registerUser_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.RegisterUser(childComplexity, args["username"].(string), args["password"].(string), args["rootPath"].(string)), true
case "Mutation.scanAll": case "Mutation.scanAll":
if e.complexity.Mutation.ScanAll == nil { if e.complexity.Mutation.ScanAll == nil {
break break
@@ -773,7 +761,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false return 0, false
} }
return e.complexity.Mutation.UpdateUser(childComplexity, args["id"].(int), args["username"].(*string), args["rootPath"].(*string), args["password"].(*string), args["admin"].(*bool)), true return e.complexity.Mutation.UpdateUser(childComplexity, args["id"].(int), args["username"].(*string), args["password"].(*string), args["admin"].(*bool)), true
case "Notification.content": case "Notification.content":
if e.complexity.Notification.Content == nil { if e.complexity.Notification.Content == nil {
@@ -1100,6 +1088,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.Admin(childComplexity), true return e.complexity.User.Admin(childComplexity), true
case "User.albums":
if e.complexity.User.Albums == nil {
break
}
return e.complexity.User.Albums(childComplexity), true
case "User.id": case "User.id":
if e.complexity.User.ID == nil { if e.complexity.User.ID == nil {
break break
@@ -1107,13 +1102,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.ID(childComplexity), true return e.complexity.User.ID(childComplexity), true
case "User.rootPath":
if e.complexity.User.RootPath == nil {
break
}
return e.complexity.User.RootPath(childComplexity), true
case "User.username": case "User.username":
if e.complexity.User.Username == nil { if e.complexity.User.Username == nil {
break break
@@ -1332,13 +1320,6 @@ type Query {
type Mutation { type Mutation {
authorizeUser(username: String!, password: String!): AuthorizeResult! authorizeUser(username: String!, password: String!): AuthorizeResult!
"Registers a new user, must be admin to call"
registerUser(
username: String!
password: String!
rootPath: String!
): AuthorizeResult!
"Registers the initial user, can only be called if initialSetup from SiteInfo is true" "Registers the initial user, can only be called if initialSetup from SiteInfo is true"
initialSetupWizard( initialSetupWizard(
username: String! username: String!
@@ -1366,13 +1347,11 @@ type Mutation {
updateUser( updateUser(
id: ID! id: ID!
username: String username: String
rootPath: String
password: String password: String
admin: Boolean admin: Boolean
): User @isAdmin ): User @isAdmin
createUser( createUser(
username: String! username: String!
rootPath: String!
password: String password: String
admin: Boolean! admin: Boolean!
): User @isAdmin ): User @isAdmin
@@ -1454,8 +1433,9 @@ type User {
id: ID! id: ID!
username: String! username: String!
#albums: [Album] #albums: [Album]
"Local filepath for the user's photos" # rootPath: String! @isAdmin
rootPath: String! @isAdmin "Top level albums owned by this user"
albums: [Album!]! @isAdmin
admin: Boolean! admin: Boolean!
#shareTokens: [ShareToken] #shareTokens: [ShareToken]
} }
@@ -1653,33 +1633,24 @@ func (ec *executionContext) field_Mutation_createUser_args(ctx context.Context,
} }
} }
args["username"] = arg0 args["username"] = arg0
var arg1 string var arg1 *string
if tmp, ok := rawArgs["rootPath"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("rootPath"))
arg1, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["rootPath"] = arg1
var arg2 *string
if tmp, ok := rawArgs["password"]; ok { if tmp, ok := rawArgs["password"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password"))
arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["password"] = arg2 args["password"] = arg1
var arg3 bool var arg2 bool
if tmp, ok := rawArgs["admin"]; ok { if tmp, ok := rawArgs["admin"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("admin")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("admin"))
arg3, err = ec.unmarshalNBoolean2bool(ctx, tmp) arg2, err = ec.unmarshalNBoolean2bool(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["admin"] = arg3 args["admin"] = arg2
return args, nil return args, nil
} }
@@ -1794,39 +1765,6 @@ func (ec *executionContext) field_Mutation_protectShareToken_args(ctx context.Co
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation_registerUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["username"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("username"))
arg0, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["username"] = arg0
var arg1 string
if tmp, ok := rawArgs["password"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password"))
arg1, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg1
var arg2 string
if tmp, ok := rawArgs["rootPath"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("rootPath"))
arg2, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["rootPath"] = arg2
return args, nil
}
func (ec *executionContext) field_Mutation_scanUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation_scanUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@@ -1960,32 +1898,23 @@ func (ec *executionContext) field_Mutation_updateUser_args(ctx context.Context,
} }
args["username"] = arg1 args["username"] = arg1
var arg2 *string var arg2 *string
if tmp, ok := rawArgs["rootPath"]; ok { if tmp, ok := rawArgs["password"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("rootPath")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password"))
arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["rootPath"] = arg2 args["password"] = arg2
var arg3 *string var arg3 *bool
if tmp, ok := rawArgs["password"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password"))
arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil {
return nil, err
}
}
args["password"] = arg3
var arg4 *bool
if tmp, ok := rawArgs["admin"]; ok { if tmp, ok := rawArgs["admin"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("admin")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("admin"))
arg4, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp) arg3, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["admin"] = arg4 args["admin"] = arg3
return args, nil return args, nil
} }
@@ -3011,14 +2940,14 @@ func (ec *executionContext) _Media_favorite(ctx context.Context, field graphql.C
Object: "Media", Object: "Media",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
IsResolver: false, IsResolver: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.Favorite, nil return ec.resolvers.Media().Favorite(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@@ -3782,48 +3711,6 @@ func (ec *executionContext) _Mutation_authorizeUser(ctx context.Context, field g
return ec.marshalNAuthorizeResult2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAuthorizeResult(ctx, field.Selections, res) return ec.marshalNAuthorizeResult2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAuthorizeResult(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_registerUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Mutation_registerUser_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().RegisterUser(rctx, args["username"].(string), args["password"].(string), args["rootPath"].(string))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*models.AuthorizeResult)
fc.Result = res
return ec.marshalNAuthorizeResult2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAuthorizeResult(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_initialSetupWizard(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Mutation_initialSetupWizard(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -4181,7 +4068,7 @@ func (ec *executionContext) _Mutation_updateUser(ctx context.Context, field grap
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().UpdateUser(rctx, args["id"].(int), args["username"].(*string), args["rootPath"].(*string), args["password"].(*string), args["admin"].(*bool)) return ec.resolvers.Mutation().UpdateUser(rctx, args["id"].(int), args["username"].(*string), args["password"].(*string), args["admin"].(*bool))
} }
directive1 := func(ctx context.Context) (interface{}, error) { directive1 := func(ctx context.Context) (interface{}, error) {
if ec.directives.IsAdmin == nil { if ec.directives.IsAdmin == nil {
@@ -4240,7 +4127,7 @@ func (ec *executionContext) _Mutation_createUser(ctx context.Context, field grap
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().CreateUser(rctx, args["username"].(string), args["rootPath"].(string), args["password"].(*string), args["admin"].(bool)) return ec.resolvers.Mutation().CreateUser(rctx, args["username"].(string), args["password"].(*string), args["admin"].(bool))
} }
directive1 := func(ctx context.Context) (interface{}, error) { directive1 := func(ctx context.Context) (interface{}, error) {
if ec.directives.IsAdmin == nil { if ec.directives.IsAdmin == nil {
@@ -6031,7 +5918,7 @@ func (ec *executionContext) _User_username(ctx context.Context, field graphql.Co
return ec.marshalNString2string(ctx, field.Selections, res) return ec.marshalNString2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _User_rootPath(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { func (ec *executionContext) _User_albums(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@@ -6050,7 +5937,7 @@ func (ec *executionContext) _User_rootPath(ctx context.Context, field graphql.Co
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.RootPath, nil return obj.Albums, nil
} }
directive1 := func(ctx context.Context) (interface{}, error) { directive1 := func(ctx context.Context) (interface{}, error) {
if ec.directives.IsAdmin == nil { if ec.directives.IsAdmin == nil {
@@ -6066,10 +5953,10 @@ func (ec *executionContext) _User_rootPath(ctx context.Context, field graphql.Co
if tmp == nil { if tmp == nil {
return nil, nil return nil, nil
} }
if data, ok := tmp.(string); ok { if data, ok := tmp.([]models.Album); ok {
return data, nil return data, nil
} }
return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp) return nil, fmt.Errorf(`unexpected type %T from directive, should be []github.com/photoview/photoview/api/graphql/models.Album`, tmp)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@@ -6081,9 +5968,9 @@ func (ec *executionContext) _User_rootPath(ctx context.Context, field graphql.Co
} }
return graphql.Null return graphql.Null
} }
res := resTmp.(string) res := resTmp.([]models.Album)
fc.Result = res fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res) return ec.marshalNAlbum2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _User_admin(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) { func (ec *executionContext) _User_admin(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
@@ -7809,10 +7696,19 @@ func (ec *executionContext) _Media(ctx context.Context, sel ast.SelectionSet, ob
case "videoMetadata": case "videoMetadata":
out.Values[i] = ec._Media_videoMetadata(ctx, field, obj) out.Values[i] = ec._Media_videoMetadata(ctx, field, obj)
case "favorite": case "favorite":
out.Values[i] = ec._Media_favorite(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Media_favorite(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "type": case "type":
out.Values[i] = ec._Media_type(ctx, field, obj) out.Values[i] = ec._Media_type(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@@ -8003,11 +7899,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "registerUser":
out.Values[i] = ec._Mutation_registerUser(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "initialSetupWizard": case "initialSetupWizard":
out.Values[i] = ec._Mutation_initialSetupWizard(ctx, field) out.Values[i] = ec._Mutation_initialSetupWizard(ctx, field)
case "scanAll": case "scanAll":
@@ -8530,8 +8421,8 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "rootPath": case "albums":
out.Values[i] = ec._User_rootPath(ctx, field, obj) out.Values[i] = ec._User_albums(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
@@ -8857,6 +8748,43 @@ func (ec *executionContext) marshalNAlbum2githubᚗcomᚋphotoviewᚋphotoview
return ec._Album(ctx, sel, &v) return ec._Album(ctx, sel, &v)
} }
func (ec *executionContext) marshalNAlbum2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx context.Context, sel ast.SelectionSet, v []models.Album) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
if !isLen1 {
wg.Add(len(v))
}
for i := range v {
i := i
fc := &graphql.FieldContext{
Index: &i,
Result: &v[i],
}
ctx := graphql.WithFieldContext(ctx, fc)
f := func(i int) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = nil
}
}()
if !isLen1 {
defer wg.Done()
}
ret[i] = ec.marshalNAlbum2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalNAlbum2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Album) graphql.Marshaler { func (ec *executionContext) marshalNAlbum2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.Album) graphql.Marshaler {
ret := make(graphql.Array, len(v)) ret := make(graphql.Array, len(v))
var wg sync.WaitGroup var wg sync.WaitGroup

View File

@@ -13,17 +13,17 @@ import (
type Media struct { type Media struct {
Model Model
Title string `gorm:"not null"` Title string `gorm:"not null"`
Path string `gorm:"not null"` Path string `gorm:"not null"`
PathHash string `gorm:"not null"` PathHash string `gorm:"not null"`
AlbumID int `gorm:"not null"` AlbumID int `gorm:"not null"`
Album Album Album Album
ExifID *int ExifID *int
Exif *MediaEXIF Exif *MediaEXIF
MediaURL []MediaURL MediaURL []MediaURL
DateShot time.Time `gorm:"not null"` DateShot time.Time `gorm:"not null"`
DateImported time.Time `gorm:"not null"` DateImported time.Time `gorm:"not null"`
Favorite bool `gorm:"not null, default:false"` // Favorite bool `gorm:"not null, default:false"`
Type MediaType `gorm:"not null"` Type MediaType `gorm:"not null"`
VideoMetadataID *int VideoMetadataID *int
VideoMetadata *VideoMetadata VideoMetadata *VideoMetadata

View File

@@ -16,13 +16,17 @@ type User struct {
Model Model
Username string `gorm:"unique,size:128"` Username string `gorm:"unique,size:128"`
Password *string `gorm:"size:256` Password *string `gorm:"size:256`
RootPath string `gorm:"size:512` // RootPath string `gorm:"size:512`
Admin bool `gorm:"default:false"` Albums []Album `gorm:"many2many:user_albums"`
Admin bool `gorm:"default:false"`
} }
// func (u *User) ID() int { type UserMediaData struct {
// return u.UserID ModelTimestamps
// } UserID int `gorm:"primaryKey;autoIncrement:false"`
MediaID int `gorm:"primaryKey;autoIncrement:false"`
Favorite bool `gorm:"not null;default:false"`
}
type AccessToken struct { type AccessToken struct {
Model Model
@@ -34,35 +38,7 @@ type AccessToken struct {
var ErrorInvalidUserCredentials = errors.New("invalid credentials") var ErrorInvalidUserCredentials = errors.New("invalid credentials")
// func NewUserFromRow(row *sql.Row) (*User, error) {
// user := User{}
// if err := row.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil {
// return nil, errors.Wrap(err, "failed to scan user from database")
// }
// return &user, nil
// }
// func NewUsersFromRows(rows *sql.Rows) ([]*User, error) {
// users := make([]*User, 0)
// for rows.Next() {
// var user User
// if err := rows.Scan(&user.UserID, &user.Username, &user.Password, &user.RootPath, &user.Admin); err != nil {
// return nil, errors.Wrap(err, "failed to scan users from database")
// }
// users = append(users, &user)
// }
// rows.Close()
// return users, nil
// }
func AuthorizeUser(db *gorm.DB, username string, password string) (*User, error) { func AuthorizeUser(db *gorm.DB, username string, password string) (*User, error) {
// row := database.QueryRow("SELECT * FROM user WHERE username = ?", username)
var user User var user User
result := db.Where("username = ?", username).First(&user) result := db.Where("username = ?", username).First(&user)
@@ -100,15 +76,15 @@ func ValidRootPath(rootPath string) bool {
return true return true
} }
func RegisterUser(db *gorm.DB, username string, password *string, rootPath string, admin bool) (*User, error) { func RegisterUser(db *gorm.DB, username string, password *string, admin bool) (*User, error) {
if !ValidRootPath(rootPath) { // if !ValidRootPath(rootPath) {
return nil, ErrorInvalidRootPath // return nil, ErrorInvalidRootPath
} // }
user := User{ user := User{
Username: username, Username: username,
RootPath: rootPath, // RootPath: rootPath,
Admin: admin, Admin: admin,
} }
if password != nil { if password != nil {
@@ -163,27 +139,12 @@ func (user *User) GenerateAccessToken(db *gorm.DB) (*AccessToken, error) {
func VerifyTokenAndGetUser(db *gorm.DB, token string) (*User, error) { func VerifyTokenAndGetUser(db *gorm.DB, token string) (*User, error) {
// row := database.QueryRow("SELECT (user_id) FROM access_token WHERE expire > ? AND value = ?", now, token)
var accessToken AccessToken var accessToken AccessToken
result := db.Where("expire > ? AND value = ?", time.Now(), token).First(&accessToken) result := db.Where("expire > ? AND value = ?", time.Now(), token).First(&accessToken)
if result.Error != nil { if result.Error != nil {
return nil, result.Error return nil, result.Error
} }
// var userId string
// if err := row.Scan(&userId); err != nil {
// log.Println(err.Error())
// return nil, err
// }
// row = db.QueryRow("SELECT * FROM user WHERE user_id = ?", userId)
// user, err := NewUserFromRow(row)
// if err != nil {
// return nil, err
// }
var user User var user User
result = db.First(&user, accessToken.UserID) result = db.First(&user, accessToken.UserID)
if result.Error != nil { if result.Error != nil {

View File

@@ -9,6 +9,7 @@ import (
"github.com/photoview/photoview/api/scanner" "github.com/photoview/photoview/api/scanner"
"github.com/pkg/errors" "github.com/pkg/errors"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause"
) )
func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*models.Media, error) { func (r *queryResolver) MyMedia(ctx context.Context, filter *models.Filter) ([]*models.Media, error) {
@@ -182,20 +183,45 @@ func (r *mediaResolver) VideoWeb(ctx context.Context, media *models.Media) (*mod
return &url, nil return &url, nil
} }
func (r *mediaResolver) Favorite(ctx context.Context, media *models.Media) (bool, error) {
user := auth.UserFromContext(ctx)
if user == nil {
return false, auth.ErrUnauthorized
}
userMediaData := models.UserMediaData{
UserID: user.ID,
MediaID: media.ID,
Favorite: false,
}
if err := r.Database.FirstOrInit(&userMediaData).Error; err != nil {
return false, errors.Wrapf(err, "get user media data from database (user: %d, media: %d)", user.ID, media.ID)
}
return userMediaData.Favorite, nil
}
func (r *mutationResolver) FavoriteMedia(ctx context.Context, mediaID int, favorite bool) (*models.Media, error) { func (r *mutationResolver) FavoriteMedia(ctx context.Context, mediaID int, favorite bool) (*models.Media, error) {
user := auth.UserFromContext(ctx) user := auth.UserFromContext(ctx)
if user == nil {
var media models.Media return nil, auth.ErrUnauthorized
if err := r.Database.Joins("Album").Where("Album.owner_id = ?", user.ID).First(&media, mediaID).Error; err != nil {
return nil, err
} }
media.Favorite = favorite userMediaData := models.UserMediaData{
UserID: user.ID,
MediaID: mediaID,
Favorite: favorite,
}
if err := r.Database.Save(&media).Error; err != nil { if err := r.Database.Clauses(clause.OnConflict{UpdateAll: true}).Create(&userMediaData).Error; err != nil {
return nil, errors.Wrap(err, "failed to update media favorite on database") return nil, errors.Wrapf(err, "update user favorite media in database")
}
var media models.Media
if err := r.Database.First(&media, mediaID).Error; err != nil {
return nil, errors.Wrap(err, "get media from database after favorite update")
} }
return &media, nil return &media, nil

View File

@@ -61,38 +61,6 @@ func (r *mutationResolver) AuthorizeUser(ctx context.Context, username string, p
Token: &token.Value, Token: &token.Value,
}, nil }, nil
} }
func (r *mutationResolver) RegisterUser(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) {
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: transactionError.Error(),
}, transactionError
}
return &models.AuthorizeResult{
Success: true,
Status: "ok",
Token: &token.Value,
}, nil
}
func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) { func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username string, password string, rootPath string) (*models.AuthorizeResult, error) {
siteInfo, err := models.GetSiteInfo(r.Database) siteInfo, err := models.GetSiteInfo(r.Database)
@@ -111,7 +79,7 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
return err return err
} }
user, err := models.RegisterUser(tx, username, &password, rootPath, true) user, err := models.RegisterUser(tx, username, &password, true)
if err != nil { if err != nil {
return err return err
} }
@@ -139,9 +107,9 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
} }
// Admin queries // Admin queries
func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *string, rootPath *string, password *string, admin *bool) (*models.User, error) { func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *string, password *string, admin *bool) (*models.User, error) {
if username == nil && rootPath == nil && password == nil && admin == nil { if username == nil && password == nil && admin == nil {
return nil, errors.New("no updates requested") return nil, errors.New("no updates requested")
} }
@@ -154,10 +122,6 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *str
user.Username = *username user.Username = *username
} }
if rootPath != nil {
user.RootPath = *rootPath
}
if password != nil { if password != nil {
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12) hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
if err != nil { if err != nil {
@@ -179,13 +143,13 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *str
return &user, nil return &user, nil
} }
func (r *mutationResolver) CreateUser(ctx context.Context, username string, rootPath string, password *string, admin bool) (*models.User, error) { func (r *mutationResolver) CreateUser(ctx context.Context, username string, password *string, admin bool) (*models.User, error) {
var user *models.User var user *models.User
transactionError := r.Database.Transaction(func(tx *gorm.DB) error { transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
var err error var err error
user, err = models.RegisterUser(tx, username, password, rootPath, admin) user, err = models.RegisterUser(tx, username, password, admin)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -58,13 +58,6 @@ type Query {
type Mutation { type Mutation {
authorizeUser(username: String!, password: String!): AuthorizeResult! authorizeUser(username: String!, password: String!): AuthorizeResult!
"Registers a new user, must be admin to call"
registerUser(
username: String!
password: String!
rootPath: String!
): AuthorizeResult!
"Registers the initial user, can only be called if initialSetup from SiteInfo is true" "Registers the initial user, can only be called if initialSetup from SiteInfo is true"
initialSetupWizard( initialSetupWizard(
username: String! username: String!
@@ -92,13 +85,11 @@ type Mutation {
updateUser( updateUser(
id: ID! id: ID!
username: String username: String
rootPath: String
password: String password: String
admin: Boolean admin: Boolean
): User @isAdmin ): User @isAdmin
createUser( createUser(
username: String! username: String!
rootPath: String!
password: String password: String
admin: Boolean! admin: Boolean!
): User @isAdmin ): User @isAdmin
@@ -180,8 +171,9 @@ type User {
id: ID! id: ID!
username: String! username: String!
#albums: [Album] #albums: [Album]
"Local filepath for the user's photos" # rootPath: String! @isAdmin
rootPath: String! @isAdmin "Top level albums owned by this user"
albums: [Album!]! @isAdmin
admin: Boolean! admin: Boolean!
#shareTokens: [ShareToken] #shareTokens: [ShareToken]
} }

View File

@@ -17,29 +17,37 @@ import (
func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScannerCache) ([]*models.Album, []error) { func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScannerCache) ([]*models.Album, []error) {
// Check if user directory exists on the file system var userRootAlbums []*models.Album
if _, err := os.Stat(user.RootPath); err != nil { if err := db.Model(&user).Association("Albums").Find(&userRootAlbums); err != nil {
if os.IsNotExist(err) { return nil, []error{errors.Wrapf(err, "get albums of user (%s)", user.Username)}
return nil, []error{errors.Errorf("Photo directory for user '%s' does not exist '%s'\n", user.Username, user.RootPath)}
} else {
return nil, []error{errors.Errorf("Could not read photo directory for user '%s': %s\n", user.Username, user.RootPath)}
}
} }
scanErrors := make([]error, 0)
type scanInfo struct { type scanInfo struct {
path string path string
parentID *int parentID *int
} }
scanQueue := list.New() scanQueue := list.New()
scanQueue.PushBack(scanInfo{
path: user.RootPath, for _, album := range userRootAlbums {
parentID: nil, // Check if user album directory exists on the file system
}) if _, err := os.Stat(album.Path); err != nil {
if os.IsNotExist(err) {
scanErrors = append(scanErrors, errors.Errorf("Album directory for user '%s' does not exist '%s'\n", user.Username, album.Path))
} else {
scanErrors = append(scanErrors, errors.Errorf("Could not read album directory for user '%s': %s\n", user.Username, album.Path))
}
} else {
scanQueue.PushBack(scanInfo{
path: album.Path,
parentID: nil,
})
}
}
userAlbums := make([]*models.Album, 0) userAlbums := make([]*models.Album, 0)
albumErrors := make([]error, 0)
// newPhotos := make([]*models.Photo, 0)
for scanQueue.Front() != nil { for scanQueue.Front() != nil {
albumInfo := scanQueue.Front().Value.(scanInfo) albumInfo := scanQueue.Front().Value.(scanInfo)
@@ -51,7 +59,7 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
// Read path // Read path
dirContent, err := ioutil.ReadDir(albumPath) dirContent, err := ioutil.ReadDir(albumPath)
if err != nil { if err != nil {
albumErrors = append(albumErrors, errors.Wrapf(err, "read directory (%s)", albumPath)) scanErrors = append(scanErrors, errors.Wrapf(err, "read directory (%s)", albumPath))
continue continue
} }
@@ -81,7 +89,7 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
}) })
if transErr != nil { if transErr != nil {
albumErrors = append(albumErrors, errors.Wrap(transErr, "begin database transaction")) scanErrors = append(scanErrors, errors.Wrap(transErr, "begin database transaction"))
continue continue
} }
@@ -104,9 +112,9 @@ func findAlbumsForUser(db *gorm.DB, user *models.User, album_cache *AlbumScanner
} }
deleteErrors := deleteOldUserAlbums(db, userAlbums, user) deleteErrors := deleteOldUserAlbums(db, userAlbums, user)
albumErrors = append(albumErrors, deleteErrors...) scanErrors = append(scanErrors, deleteErrors...)
return userAlbums, albumErrors return userAlbums, scanErrors
} }
func directoryContainsPhotos(rootPath string, cache *AlbumScannerCache) bool { func directoryContainsPhotos(rootPath string, cache *AlbumScannerCache) bool {