Add user management to api
This commit is contained in:
parent
108de3d6b8
commit
a87e9fc56d
|
@ -1,7 +1,7 @@
|
||||||
CREATE TABLE IF NOT EXISTS user (
|
CREATE TABLE IF NOT EXISTS user (
|
||||||
user_id int NOT NULL AUTO_INCREMENT,
|
user_id int NOT NULL AUTO_INCREMENT,
|
||||||
username varchar(256) NOT NULL UNIQUE,
|
username varchar(256) NOT NULL UNIQUE,
|
||||||
password varchar(256) NOT NULL,
|
password varchar(256),
|
||||||
root_path varchar(512),
|
root_path varchar(512),
|
||||||
admin boolean NOT NULL DEFAULT 0,
|
admin boolean NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
|
|
@ -69,13 +69,16 @@ 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
|
||||||
DeleteShareToken func(childComplexity int, token string) int
|
DeleteShareToken func(childComplexity int, token string) int
|
||||||
|
DeleteUser func(childComplexity int, id int) int
|
||||||
InitialSetupWizard func(childComplexity int, username string, password string, rootPath string) int
|
InitialSetupWizard func(childComplexity int, username string, password string, rootPath string) int
|
||||||
RegisterUser func(childComplexity int, username string, password string, rootPath 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
|
||||||
ShareAlbum func(childComplexity int, albumID int, expire *time.Time, password *string) int
|
ShareAlbum func(childComplexity int, albumID int, expire *time.Time, password *string) int
|
||||||
SharePhoto func(childComplexity int, photoID int, expire *time.Time, password *string) int
|
SharePhoto func(childComplexity int, photoID int, expire *time.Time, password *string) int
|
||||||
|
UpdateUser func(childComplexity int, id int, username *string, rootPath *string, admin *bool) int
|
||||||
}
|
}
|
||||||
|
|
||||||
Photo struct {
|
Photo struct {
|
||||||
|
@ -172,6 +175,9 @@ type MutationResolver interface {
|
||||||
ShareAlbum(ctx context.Context, albumID int, expire *time.Time, password *string) (*models.ShareToken, error)
|
ShareAlbum(ctx context.Context, albumID int, expire *time.Time, password *string) (*models.ShareToken, error)
|
||||||
SharePhoto(ctx context.Context, photoID int, expire *time.Time, password *string) (*models.ShareToken, error)
|
SharePhoto(ctx context.Context, photoID int, expire *time.Time, password *string) (*models.ShareToken, error)
|
||||||
DeleteShareToken(ctx context.Context, token string) (*models.ShareToken, error)
|
DeleteShareToken(ctx context.Context, token string) (*models.ShareToken, error)
|
||||||
|
UpdateUser(ctx context.Context, id int, username *string, rootPath *string, admin *bool) (*models.User, error)
|
||||||
|
CreateUser(ctx context.Context, username string, rootPath string, password *string, admin bool) (*models.User, error)
|
||||||
|
DeleteUser(ctx context.Context, id int) (*models.User, error)
|
||||||
}
|
}
|
||||||
type PhotoResolver interface {
|
type PhotoResolver interface {
|
||||||
Thumbnail(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error)
|
Thumbnail(ctx context.Context, obj *models.Photo) (*models.PhotoURL, error)
|
||||||
|
@ -319,6 +325,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Mutation.AuthorizeUser(childComplexity, args["username"].(string), args["password"].(string)), true
|
return e.complexity.Mutation.AuthorizeUser(childComplexity, args["username"].(string), args["password"].(string)), true
|
||||||
|
|
||||||
|
case "Mutation.createUser":
|
||||||
|
if e.complexity.Mutation.CreateUser == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_createUser_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.CreateUser(childComplexity, args["username"].(string), args["rootPath"].(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 {
|
||||||
break
|
break
|
||||||
|
@ -331,6 +349,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Mutation.DeleteShareToken(childComplexity, args["token"].(string)), true
|
return e.complexity.Mutation.DeleteShareToken(childComplexity, args["token"].(string)), true
|
||||||
|
|
||||||
|
case "Mutation.deleteUser":
|
||||||
|
if e.complexity.Mutation.DeleteUser == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_deleteUser_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.DeleteUser(childComplexity, args["id"].(int)), true
|
||||||
|
|
||||||
case "Mutation.initialSetupWizard":
|
case "Mutation.initialSetupWizard":
|
||||||
if e.complexity.Mutation.InitialSetupWizard == nil {
|
if e.complexity.Mutation.InitialSetupWizard == nil {
|
||||||
break
|
break
|
||||||
|
@ -398,6 +428,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Mutation.SharePhoto(childComplexity, args["photoId"].(int), args["expire"].(*time.Time), args["password"].(*string)), true
|
return e.complexity.Mutation.SharePhoto(childComplexity, args["photoId"].(int), args["expire"].(*time.Time), args["password"].(*string)), true
|
||||||
|
|
||||||
|
case "Mutation.updateUser":
|
||||||
|
if e.complexity.Mutation.UpdateUser == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_updateUser_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.UpdateUser(childComplexity, args["id"].(int), args["username"].(*string), args["rootPath"].(*string), args["admin"].(*bool)), true
|
||||||
|
|
||||||
case "Photo.album":
|
case "Photo.album":
|
||||||
if e.complexity.Photo.Album == nil {
|
if e.complexity.Photo.Album == nil {
|
||||||
break
|
break
|
||||||
|
@ -881,7 +923,7 @@ type Mutation {
|
||||||
): AuthorizeResult
|
): AuthorizeResult
|
||||||
|
|
||||||
"Scan all users for new photos"
|
"Scan all users for new photos"
|
||||||
scanAll: ScannerResult!
|
scanAll: ScannerResult! @isAdmin
|
||||||
"Scan a single user for new photos"
|
"Scan a single user for new photos"
|
||||||
scanUser(userId: Int!): ScannerResult!
|
scanUser(userId: Int!): ScannerResult!
|
||||||
|
|
||||||
|
@ -891,6 +933,20 @@ type Mutation {
|
||||||
sharePhoto(photoId: Int!, expire: Time, password: String): ShareToken
|
sharePhoto(photoId: Int!, expire: Time, password: String): ShareToken
|
||||||
"Delete a share token by it's token value"
|
"Delete a share token by it's token value"
|
||||||
deleteShareToken(token: String!): ShareToken
|
deleteShareToken(token: String!): ShareToken
|
||||||
|
|
||||||
|
updateUser(
|
||||||
|
id: Int!
|
||||||
|
username: String
|
||||||
|
rootPath: String
|
||||||
|
admin: Boolean
|
||||||
|
): User @isAdmin
|
||||||
|
createUser(
|
||||||
|
username: String!
|
||||||
|
rootPath: String!
|
||||||
|
password: String
|
||||||
|
admin: Boolean!
|
||||||
|
): User @isAdmin
|
||||||
|
deleteUser(id: Int!): User @isAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizeResult {
|
type AuthorizeResult {
|
||||||
|
@ -1066,6 +1122,44 @@ func (ec *executionContext) field_Mutation_authorizeUser_args(ctx context.Contex
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_createUser_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 {
|
||||||
|
arg0, err = ec.unmarshalNString2string(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["username"] = arg0
|
||||||
|
var arg1 string
|
||||||
|
if tmp, ok := rawArgs["rootPath"]; ok {
|
||||||
|
arg1, err = ec.unmarshalNString2string(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["rootPath"] = arg1
|
||||||
|
var arg2 *string
|
||||||
|
if tmp, ok := rawArgs["password"]; ok {
|
||||||
|
arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["password"] = arg2
|
||||||
|
var arg3 bool
|
||||||
|
if tmp, ok := rawArgs["admin"]; ok {
|
||||||
|
arg3, err = ec.unmarshalNBoolean2bool(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["admin"] = arg3
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_deleteShareToken_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_deleteShareToken_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{}{}
|
||||||
|
@ -1080,6 +1174,20 @@ func (ec *executionContext) field_Mutation_deleteShareToken_args(ctx context.Con
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_deleteUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 int
|
||||||
|
if tmp, ok := rawArgs["id"]; ok {
|
||||||
|
arg0, err = ec.unmarshalNInt2int(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["id"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_initialSetupWizard_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_initialSetupWizard_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{}{}
|
||||||
|
@ -1214,6 +1322,44 @@ func (ec *executionContext) field_Mutation_sharePhoto_args(ctx context.Context,
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_updateUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 int
|
||||||
|
if tmp, ok := rawArgs["id"]; ok {
|
||||||
|
arg0, err = ec.unmarshalNInt2int(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["id"] = arg0
|
||||||
|
var arg1 *string
|
||||||
|
if tmp, ok := rawArgs["username"]; ok {
|
||||||
|
arg1, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["username"] = arg1
|
||||||
|
var arg2 *string
|
||||||
|
if tmp, ok := rawArgs["rootPath"]; ok {
|
||||||
|
arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["rootPath"] = arg2
|
||||||
|
var arg3 *bool
|
||||||
|
if tmp, ok := rawArgs["admin"]; ok {
|
||||||
|
arg3, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["admin"] = arg3
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Query___type_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{}{}
|
||||||
|
@ -1949,8 +2095,28 @@ func (ec *executionContext) _Mutation_scanAll(ctx context.Context, field graphql
|
||||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
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
|
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||||
return ec.resolvers.Mutation().ScanAll(rctx)
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().ScanAll(rctx)
|
||||||
|
}
|
||||||
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
|
if ec.directives.IsAdmin == nil {
|
||||||
|
return nil, errors.New("directive isAdmin is not implemented")
|
||||||
|
}
|
||||||
|
return ec.directives.IsAdmin(ctx, nil, directive0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := directive1(rctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tmp == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if data, ok := tmp.(*models.ScannerResult); ok {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/viktorstrate/photoview/api/graphql/models.ScannerResult`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -2135,6 +2301,189 @@ func (ec *executionContext) _Mutation_deleteShareToken(ctx context.Context, fiel
|
||||||
return ec.marshalOShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
|
return ec.marshalOShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_updateUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
ec.Tracer.EndFieldExecution(ctx)
|
||||||
|
}()
|
||||||
|
rctx := &graphql.ResolverContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
}
|
||||||
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_updateUser_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
rctx.Args = args
|
||||||
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||||
|
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["admin"].(*bool))
|
||||||
|
}
|
||||||
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
|
if ec.directives.IsAdmin == nil {
|
||||||
|
return nil, errors.New("directive isAdmin is not implemented")
|
||||||
|
}
|
||||||
|
return ec.directives.IsAdmin(ctx, nil, directive0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := directive1(rctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tmp == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if data, ok := tmp.(*models.User); ok {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/viktorstrate/photoview/api/graphql/models.User`, tmp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*models.User)
|
||||||
|
rctx.Result = res
|
||||||
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
return ec.marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_createUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
ec.Tracer.EndFieldExecution(ctx)
|
||||||
|
}()
|
||||||
|
rctx := &graphql.ResolverContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
}
|
||||||
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_createUser_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
rctx.Args = args
|
||||||
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
|
if ec.directives.IsAdmin == nil {
|
||||||
|
return nil, errors.New("directive isAdmin is not implemented")
|
||||||
|
}
|
||||||
|
return ec.directives.IsAdmin(ctx, nil, directive0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := directive1(rctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tmp == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if data, ok := tmp.(*models.User); ok {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/viktorstrate/photoview/api/graphql/models.User`, tmp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*models.User)
|
||||||
|
rctx.Result = res
|
||||||
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
return ec.marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_deleteUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
ec.Tracer.EndFieldExecution(ctx)
|
||||||
|
}()
|
||||||
|
rctx := &graphql.ResolverContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
}
|
||||||
|
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_deleteUser_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
rctx.Args = args
|
||||||
|
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().DeleteUser(rctx, args["id"].(int))
|
||||||
|
}
|
||||||
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
|
if ec.directives.IsAdmin == nil {
|
||||||
|
return nil, errors.New("directive isAdmin is not implemented")
|
||||||
|
}
|
||||||
|
return ec.directives.IsAdmin(ctx, nil, directive0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err := directive1(rctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tmp == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if data, ok := tmp.(*models.User); ok {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/viktorstrate/photoview/api/graphql/models.User`, tmp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*models.User)
|
||||||
|
rctx.Result = res
|
||||||
|
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||||
|
return ec.marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Photo_id(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Photo_id(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) {
|
||||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -5401,6 +5750,12 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||||
out.Values[i] = ec._Mutation_sharePhoto(ctx, field)
|
out.Values[i] = ec._Mutation_sharePhoto(ctx, field)
|
||||||
case "deleteShareToken":
|
case "deleteShareToken":
|
||||||
out.Values[i] = ec._Mutation_deleteShareToken(ctx, field)
|
out.Values[i] = ec._Mutation_deleteShareToken(ctx, field)
|
||||||
|
case "updateUser":
|
||||||
|
out.Values[i] = ec._Mutation_updateUser(ctx, field)
|
||||||
|
case "createUser":
|
||||||
|
out.Values[i] = ec._Mutation_createUser(ctx, field)
|
||||||
|
case "deleteUser":
|
||||||
|
out.Values[i] = ec._Mutation_deleteUser(ctx, field)
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
|
@ -7030,6 +7385,17 @@ func (ec *executionContext) marshalOTime2ᚖtimeᚐTime(ctx context.Context, sel
|
||||||
return ec.marshalOTime2timeᚐTime(ctx, sel, *v)
|
return ec.marshalOTime2timeᚐTime(ctx, sel, *v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOUser2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v models.User) graphql.Marshaler {
|
||||||
|
return ec._User(ctx, sel, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v *models.User) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return ec._User(ctx, sel, v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler {
|
func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -14,7 +15,7 @@ import (
|
||||||
type User struct {
|
type User struct {
|
||||||
UserID int
|
UserID int
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password *string
|
||||||
RootPath string
|
RootPath string
|
||||||
Admin bool
|
Admin bool
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,11 @@ func AuthorizeUser(database *sql.DB, username string, password string) (*User, e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
if user.Password == nil {
|
||||||
|
return nil, errors.New("user does not have a password")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(password)); err != nil {
|
||||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
return nil, ErrorInvalidUserCredentials
|
return nil, ErrorInvalidUserCredentials
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,15 +82,37 @@ func AuthorizeUser(database *sql.DB, username string, password string) (*User, e
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterUser(database *sql.Tx, username string, password string, rootPath string) (*User, error) {
|
var ErrorInvalidRootPath = errors.New("invalid root path")
|
||||||
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hashedPass := string(hashedPassBytes)
|
|
||||||
|
|
||||||
if _, err := database.Exec("INSERT INTO user (username, password, root_path) VALUES (?, ?, ?)", username, hashedPass, rootPath); err != nil {
|
func ValidRootPath(rootPath string) bool {
|
||||||
return nil, err
|
_, err := os.Stat(rootPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warn: invalid root path: '%s'\n%s\n", rootPath, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterUser(database *sql.Tx, username string, password *string, rootPath string, admin bool) (*User, error) {
|
||||||
|
if !ValidRootPath(rootPath) {
|
||||||
|
return nil, ErrorInvalidRootPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if password != nil {
|
||||||
|
hashedPassBytes, err := bcrypt.GenerateFromPassword([]byte(*password), 12)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashedPass := string(hashedPassBytes)
|
||||||
|
|
||||||
|
if _, err := database.Exec("INSERT INTO user (username, password, root_path, admin) VALUES (?, ?, ?, ?)", username, hashedPass, rootPath, admin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := database.Exec("INSERT INTO user (username, root_path, admin) VALUES (?, ?, ?)", username, rootPath, admin); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
row := database.QueryRow("SELECT * FROM user WHERE username = ?", username)
|
row := database.QueryRow("SELECT * FROM user WHERE username = ?", username)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package resolvers
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||||
|
@ -81,7 +82,7 @@ func (r *mutationResolver) RegisterUser(ctx context.Context, username string, pa
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := models.RegisterUser(tx, username, password, rootPath)
|
user, err := models.RegisterUser(tx, username, &password, rootPath, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return &models.AuthorizeResult{
|
return &models.AuthorizeResult{
|
||||||
|
@ -127,7 +128,7 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := models.RegisterUser(tx, username, password, rootPath)
|
user, err := models.RegisterUser(tx, username, &password, rootPath, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return &models.AuthorizeResult{
|
return &models.AuthorizeResult{
|
||||||
|
@ -136,11 +137,6 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := tx.Exec("UPDATE user SET admin = true WHERE user_id = ?", user.UserID); err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := user.GenerateAccessToken(tx)
|
token, err := user.GenerateAccessToken(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -157,3 +153,108 @@ func (r *mutationResolver) InitialSetupWizard(ctx context.Context, username stri
|
||||||
Token: &token.Value,
|
Token: &token.Value,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Admin queries
|
||||||
|
func (r *mutationResolver) UpdateUser(ctx context.Context, id int, username *string, rootPath *string, admin *bool) (*models.User, error) {
|
||||||
|
|
||||||
|
user_rows, err := r.Database.Query("SELECT * FROM user WHERE user_id = ?", id)
|
||||||
|
if 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)
|
||||||
|
}
|
||||||
|
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 len(update_str) == 0 {
|
||||||
|
return nil, errors.New("no updates requested")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := r.Database.Exec("DELETE FROM user WHERE user_id = ?", id)
|
||||||
|
if 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
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ type Mutation {
|
||||||
): AuthorizeResult
|
): AuthorizeResult
|
||||||
|
|
||||||
"Scan all users for new photos"
|
"Scan all users for new photos"
|
||||||
scanAll: ScannerResult!
|
scanAll: ScannerResult! @isAdmin
|
||||||
"Scan a single user for new photos"
|
"Scan a single user for new photos"
|
||||||
scanUser(userId: Int!): ScannerResult!
|
scanUser(userId: Int!): ScannerResult!
|
||||||
|
|
||||||
|
@ -63,6 +63,20 @@ type Mutation {
|
||||||
sharePhoto(photoId: Int!, expire: Time, password: String): ShareToken
|
sharePhoto(photoId: Int!, expire: Time, password: String): ShareToken
|
||||||
"Delete a share token by it's token value"
|
"Delete a share token by it's token value"
|
||||||
deleteShareToken(token: String!): ShareToken
|
deleteShareToken(token: String!): ShareToken
|
||||||
|
|
||||||
|
updateUser(
|
||||||
|
id: Int!
|
||||||
|
username: String
|
||||||
|
rootPath: String
|
||||||
|
admin: Boolean
|
||||||
|
): User @isAdmin
|
||||||
|
createUser(
|
||||||
|
username: String!
|
||||||
|
rootPath: String!
|
||||||
|
password: String
|
||||||
|
admin: Boolean!
|
||||||
|
): User @isAdmin
|
||||||
|
deleteUser(id: Int!): User @isAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorizeResult {
|
type AuthorizeResult {
|
||||||
|
|
|
@ -5,7 +5,11 @@ import { Table, Button, Input, Checkbox } from 'semantic-ui-react'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
const createUserMutation = gql`
|
const createUserMutation = gql`
|
||||||
mutation createUser($username: String, $rootPath: String, $admin: Boolean) {
|
mutation createUser(
|
||||||
|
$username: String!
|
||||||
|
$rootPath: String!
|
||||||
|
$admin: Boolean!
|
||||||
|
) {
|
||||||
createUser(username: $username, rootPath: $rootPath, admin: $admin) {
|
createUser(username: $username, rootPath: $rootPath, admin: $admin) {
|
||||||
id
|
id
|
||||||
username
|
username
|
||||||
|
|
Loading…
Reference in New Issue