MediaItem Upload Support

This commit is contained in:
Evan Reichard 2021-02-05 22:01:51 -05:00
parent a5692babb8
commit 548a50278a
9 changed files with 505 additions and 376 deletions

View File

@ -73,7 +73,6 @@ type ComplexityRoot struct {
RefreshKey func(childComplexity int) int RefreshKey func(childComplexity int) int
Type func(childComplexity int) int Type func(childComplexity int) int
UpdatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int
User func(childComplexity int) int
UserID func(childComplexity int) int UserID func(childComplexity int) int
} }
@ -94,7 +93,6 @@ type ComplexityRoot struct {
OrigName func(childComplexity int) int OrigName func(childComplexity int) int
Tags func(childComplexity int) int Tags func(childComplexity int) int
UpdatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int
User func(childComplexity int) int
UserID func(childComplexity int) int UserID func(childComplexity int) int
} }
@ -105,7 +103,6 @@ type ComplexityRoot struct {
Mutation struct { Mutation struct {
CreateAlbum func(childComplexity int, input model.NewAlbum) int CreateAlbum func(childComplexity int, input model.NewAlbum) int
CreateDevice func(childComplexity int, input model.NewDevice) int
CreateMediaItem func(childComplexity int, input model.NewMediaItem) int CreateMediaItem func(childComplexity int, input model.NewMediaItem) int
CreateTag func(childComplexity int, input model.NewTag) int CreateTag func(childComplexity int, input model.NewTag) int
CreateUser func(childComplexity int, input model.NewUser) int CreateUser func(childComplexity int, input model.NewUser) int
@ -146,16 +143,18 @@ type ComplexityRoot struct {
} }
User struct { User struct {
AuthType func(childComplexity int) int AuthType func(childComplexity int) int
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
Email func(childComplexity int) int Devices func(childComplexity int) int
FirstName func(childComplexity int) int Email func(childComplexity int) int
ID func(childComplexity int) int FirstName func(childComplexity int) int
LastName func(childComplexity int) int ID func(childComplexity int) int
Password func(childComplexity int) int LastName func(childComplexity int) int
Role func(childComplexity int) int MediaItems func(childComplexity int) int
UpdatedAt func(childComplexity int) int Password func(childComplexity int) int
Username func(childComplexity int) int Role func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Username func(childComplexity int) int
} }
UserResponse struct { UserResponse struct {
@ -166,7 +165,6 @@ type ComplexityRoot struct {
type MutationResolver interface { type MutationResolver interface {
CreateMediaItem(ctx context.Context, input model.NewMediaItem) (*model.MediaItem, error) CreateMediaItem(ctx context.Context, input model.NewMediaItem) (*model.MediaItem, error)
CreateDevice(ctx context.Context, input model.NewDevice) (*model.Device, error)
CreateAlbum(ctx context.Context, input model.NewAlbum) (*model.Album, error) CreateAlbum(ctx context.Context, input model.NewAlbum) (*model.Album, error)
CreateTag(ctx context.Context, input model.NewTag) (*model.Tag, error) CreateTag(ctx context.Context, input model.NewTag) (*model.Tag, error)
CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error)
@ -307,13 +305,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Device.UpdatedAt(childComplexity), true return e.complexity.Device.UpdatedAt(childComplexity), true
case "Device.user":
if e.complexity.Device.User == nil {
break
}
return e.complexity.Device.User(childComplexity), true
case "Device.userID": case "Device.userID":
if e.complexity.Device.UserID == nil { if e.complexity.Device.UserID == nil {
break break
@ -412,13 +403,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.MediaItem.UpdatedAt(childComplexity), true return e.complexity.MediaItem.UpdatedAt(childComplexity), true
case "MediaItem.user":
if e.complexity.MediaItem.User == nil {
break
}
return e.complexity.MediaItem.User(childComplexity), true
case "MediaItem.userID": case "MediaItem.userID":
if e.complexity.MediaItem.UserID == nil { if e.complexity.MediaItem.UserID == nil {
break break
@ -452,18 +436,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.CreateAlbum(childComplexity, args["input"].(model.NewAlbum)), true return e.complexity.Mutation.CreateAlbum(childComplexity, args["input"].(model.NewAlbum)), true
case "Mutation.createDevice":
if e.complexity.Mutation.CreateDevice == nil {
break
}
args, err := ec.field_Mutation_createDevice_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.CreateDevice(childComplexity, args["input"].(model.NewDevice)), true
case "Mutation.createMediaItem": case "Mutation.createMediaItem":
if e.complexity.Mutation.CreateMediaItem == nil { if e.complexity.Mutation.CreateMediaItem == nil {
break break
@ -723,6 +695,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.CreatedAt(childComplexity), true return e.complexity.User.CreatedAt(childComplexity), true
case "User.devices":
if e.complexity.User.Devices == nil {
break
}
return e.complexity.User.Devices(childComplexity), true
case "User.email": case "User.email":
if e.complexity.User.Email == nil { if e.complexity.User.Email == nil {
break break
@ -751,6 +730,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.LastName(childComplexity), true return e.complexity.User.LastName(childComplexity), true
case "User.mediaItems":
if e.complexity.User.MediaItems == nil {
break
}
return e.complexity.User.MediaItems(childComplexity), true
case "User.password": case "User.password":
if e.complexity.User.Password == nil { if e.complexity.User.Password == nil {
break break
@ -998,6 +984,21 @@ input AuthTypeFilter {
# -------------------- Object Definitions -------------------- # -------------------- Object Definitions --------------------
# ------------------------------------------------------------ # ------------------------------------------------------------
type User {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
email: String! @meta(gorm: "not null;unique")
username: String! @meta(gorm: "not null;unique")
firstName: String
lastName: String
role: Role! @meta(gorm: "default:User;not null")
authType: AuthType! @meta(gorm: "default:Local;not null")
password: String @isPrivate
devices: [Device!] @meta(gorm: "foreignKey:UserID")
mediaItems: [MediaItem!] @meta(gorm: "foreignKey:UserID")
}
type Device { type Device {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
@ -1005,21 +1006,7 @@ type Device {
name: String! @meta(gorm: "not null") name: String! @meta(gorm: "not null")
type: DeviceType! @meta(gorm: "default:Unknown;not null") type: DeviceType! @meta(gorm: "default:Unknown;not null")
userID: ID! @meta(gorm: "not null") userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null") refreshKey: String @isPrivate
refreshKey: String @deprecated(reason: "Private Field") # @isPrivate
}
type User {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
email: String! @meta(gorm: "not null;unique")
username: String! @meta(gorm: "not null;unique")
firstName: String
lastName: String
role: Role! @meta(gorm: "default:User;not null")
authType: AuthType! @meta(gorm: "default:Local;not null")
password: String @deprecated(reason: "Private Field") #@isPrivate
} }
type MediaItem { type MediaItem {
@ -1035,21 +1022,20 @@ type MediaItem {
tags: [Tag] @meta(gorm: "many2many:media_tags") tags: [Tag] @meta(gorm: "many2many:media_tags")
albums: [Album] @meta(gorm: "many2many:media_albums") albums: [Album] @meta(gorm: "many2many:media_albums")
userID: ID! @meta(gorm: "not null") userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
} }
type Tag { type Tag {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
updatedAt: Time updatedAt: Time
name: String! @meta(gorm: "unique;not null") name: String! @meta(gorm: "unique;not null")
} }
type Album { type Album {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
updatedAt: Time updatedAt: Time
name: String! @meta(gorm: "unique;not null") name: String! @meta(gorm: "unique;not null")
} }
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -1131,10 +1117,6 @@ input NewUser {
password: String password: String
} }
input NewDevice {
name: String!
}
input NewMediaItem { input NewMediaItem {
file: Upload! file: Upload!
tags: [ID!] tags: [ID!]
@ -1236,7 +1218,6 @@ type Query {
type Mutation { type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User) createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createDevice(input: NewDevice!): Device! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User) createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasMinRole(role: User) createTag(input: NewTag!): Tag! @hasMinRole(role: User)
createUser(input: NewUser!): User! @hasMinRole(role: Admin) createUser(input: NewUser!): User! @hasMinRole(role: Admin)
@ -1294,21 +1275,6 @@ func (ec *executionContext) field_Mutation_createAlbum_args(ctx context.Context,
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation_createDevice_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.NewDevice
if tmp, ok := rawArgs["input"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
arg0, err = ec.unmarshalNNewDevice2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐNewDevice(ctx, tmp)
if err != nil {
return nil, err
}
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_createMediaItem_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation_createMediaItem_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{}{}
@ -2328,7 +2294,7 @@ func (ec *executionContext) _Device_userID(ctx context.Context, field graphql.Co
return ec.marshalNID2string(ctx, field.Selections, res) return ec.marshalNID2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _Device_user(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) { func (ec *executionContext) _Device_refreshKey(ctx context.Context, field graphql.CollectedField, obj *model.Device) (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))
@ -2347,17 +2313,13 @@ func (ec *executionContext) _Device_user(ctx context.Context, field graphql.Coll
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.User, nil return obj.RefreshKey, nil
} }
directive1 := func(ctx context.Context) (interface{}, error) { directive1 := func(ctx context.Context) (interface{}, error) {
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "foreignKey:ID;references:UserID;not null") if ec.directives.IsPrivate == nil {
if err != nil { return nil, errors.New("directive isPrivate is not implemented")
return nil, err
} }
if ec.directives.Meta == nil { return ec.directives.IsPrivate(ctx, obj, directive0)
return nil, errors.New("directive meta is not implemented")
}
return ec.directives.Meta(ctx, obj, directive0, gorm)
} }
tmp, err := directive1(rctx) tmp, err := directive1(rctx)
@ -2367,45 +2329,10 @@ func (ec *executionContext) _Device_user(ctx context.Context, field graphql.Coll
if tmp == nil { if tmp == nil {
return nil, nil return nil, nil
} }
if data, ok := tmp.(*model.User); ok { if data, ok := tmp.(*string); ok {
return data, nil return data, nil
} }
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.User`, tmp) return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
})
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.(*model.User)
fc.Result = res
return ec.marshalNUser2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
}
func (ec *executionContext) _Device_refreshKey(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Device",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.RefreshKey, nil
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -3053,65 +2980,6 @@ func (ec *executionContext) _MediaItem_userID(ctx context.Context, field graphql
return ec.marshalNID2string(ctx, field.Selections, res) return ec.marshalNID2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _MediaItem_user(ctx context.Context, field graphql.CollectedField, obj *model.MediaItem) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "MediaItem",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
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 obj.User, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "foreignKey:ID;references:UserID;not null")
if err != nil {
return nil, err
}
if ec.directives.Meta == nil {
return nil, errors.New("directive meta is not implemented")
}
return ec.directives.Meta(ctx, obj, directive0, gorm)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.User); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.User`, tmp)
})
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.(*model.User)
fc.Result = res
return ec.marshalNUser2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
}
func (ec *executionContext) _MediaItemResponse_data(ctx context.Context, field graphql.CollectedField, obj *model.MediaItemResponse) (ret graphql.Marshaler) { func (ec *executionContext) _MediaItemResponse_data(ctx context.Context, field graphql.CollectedField, obj *model.MediaItemResponse) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -3245,72 +3113,6 @@ func (ec *executionContext) _Mutation_createMediaItem(ctx context.Context, field
return ec.marshalNMediaItem2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItem(ctx, field.Selections, res) return ec.marshalNMediaItem2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItem(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_createDevice(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_createDevice_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) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().CreateDevice(rctx, args["input"].(model.NewDevice))
}
directive1 := func(ctx context.Context) (interface{}, error) {
role, err := ec.unmarshalNRole2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐRole(ctx, "User")
if err != nil {
return nil, err
}
if ec.directives.HasMinRole == nil {
return nil, errors.New("directive hasMinRole is not implemented")
}
return ec.directives.HasMinRole(ctx, nil, directive0, role)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.Device); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.Device`, tmp)
})
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.(*model.Device)
fc.Result = res
return ec.marshalNDevice2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐDevice(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_createAlbum(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Mutation_createAlbum(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -5194,8 +4996,28 @@ func (ec *executionContext) _User_password(ctx context.Context, field graphql.Co
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 directive0 := func(rctx context.Context) (interface{}, error) {
return obj.Password, nil ctx = rctx // use context from middleware stack in children
return obj.Password, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
if ec.directives.IsPrivate == nil {
return nil, errors.New("directive isPrivate is not implemented")
}
return ec.directives.IsPrivate(ctx, obj, directive0)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*string); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -5209,6 +5031,118 @@ func (ec *executionContext) _User_password(ctx context.Context, field graphql.Co
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _User_devices(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "User",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
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 obj.Devices, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "foreignKey:UserID")
if err != nil {
return nil, err
}
if ec.directives.Meta == nil {
return nil, errors.New("directive meta is not implemented")
}
return ec.directives.Meta(ctx, obj, directive0, gorm)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.Device); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*reichard.io/imagini/graph/model.Device`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.([]*model.Device)
fc.Result = res
return ec.marshalODevice2ᚕᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐDeviceᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _User_mediaItems(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "User",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
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 obj.MediaItems, nil
}
directive1 := func(ctx context.Context) (interface{}, error) {
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "foreignKey:UserID")
if err != nil {
return nil, err
}
if ec.directives.Meta == nil {
return nil, errors.New("directive meta is not implemented")
}
return ec.directives.Meta(ctx, obj, directive0, gorm)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*model.MediaItem); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*reichard.io/imagini/graph/model.MediaItem`, tmp)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.([]*model.MediaItem)
fc.Result = res
return ec.marshalOMediaItem2ᚕᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItemᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _UserResponse_data(ctx context.Context, field graphql.CollectedField, obj *model.UserResponse) (ret graphql.Marshaler) { func (ec *executionContext) _UserResponse_data(ctx context.Context, field graphql.CollectedField, obj *model.UserResponse) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -6931,26 +6865,6 @@ func (ec *executionContext) unmarshalInputNewAlbum(ctx context.Context, obj inte
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputNewDevice(ctx context.Context, obj interface{}) (model.NewDevice, error) {
var it model.NewDevice
var asMap = obj.(map[string]interface{})
for k, v := range asMap {
switch k {
case "name":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name"))
it.Name, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputNewMediaItem(ctx context.Context, obj interface{}) (model.NewMediaItem, error) { func (ec *executionContext) unmarshalInputNewMediaItem(ctx context.Context, obj interface{}) (model.NewMediaItem, error) {
var it model.NewMediaItem var it model.NewMediaItem
var asMap = obj.(map[string]interface{}) var asMap = obj.(map[string]interface{})
@ -7610,11 +7524,6 @@ func (ec *executionContext) _Device(ctx context.Context, sel ast.SelectionSet, o
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "user":
out.Values[i] = ec._Device_user(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "refreshKey": case "refreshKey":
out.Values[i] = ec._Device_refreshKey(ctx, field, obj) out.Values[i] = ec._Device_refreshKey(ctx, field, obj)
default: default:
@ -7707,11 +7616,6 @@ func (ec *executionContext) _MediaItem(ctx context.Context, sel ast.SelectionSet
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "user":
out.Values[i] = ec._MediaItem_user(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -7772,11 +7676,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 "createDevice":
out.Values[i] = ec._Mutation_createDevice(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "createAlbum": case "createAlbum":
out.Values[i] = ec._Mutation_createAlbum(ctx, field) out.Values[i] = ec._Mutation_createAlbum(ctx, field)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -8163,6 +8062,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
} }
case "password": case "password":
out.Values[i] = ec._User_password(ctx, field, obj) out.Values[i] = ec._User_password(ctx, field, obj)
case "devices":
out.Values[i] = ec._User_devices(ctx, field, obj)
case "mediaItems":
out.Values[i] = ec._User_mediaItems(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -8641,11 +8544,6 @@ func (ec *executionContext) unmarshalNNewAlbum2reichardᚗioᚋimaginiᚋgraph
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
} }
func (ec *executionContext) unmarshalNNewDevice2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐNewDevice(ctx context.Context, v interface{}) (model.NewDevice, error) {
res, err := ec.unmarshalInputNewDevice(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalNNewMediaItem2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐNewMediaItem(ctx context.Context, v interface{}) (model.NewMediaItem, error) { func (ec *executionContext) unmarshalNNewMediaItem2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐNewMediaItem(ctx context.Context, v interface{}) (model.NewMediaItem, error) {
res, err := ec.unmarshalInputNewMediaItem(ctx, v) res, err := ec.unmarshalInputNewMediaItem(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
@ -9211,6 +9109,46 @@ func (ec *executionContext) marshalODevice2ᚕᚖreichardᚗioᚋimaginiᚋgraph
return ret return ret
} }
func (ec *executionContext) marshalODevice2ᚕᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐDeviceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Device) graphql.Marshaler {
if v == nil {
return graphql.Null
}
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.marshalNDevice2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐDevice(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalODevice2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐDevice(ctx context.Context, sel ast.SelectionSet, v *model.Device) graphql.Marshaler { func (ec *executionContext) marshalODevice2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐDevice(ctx context.Context, sel ast.SelectionSet, v *model.Device) graphql.Marshaler {
if v == nil { if v == nil {
return graphql.Null return graphql.Null
@ -9523,6 +9461,46 @@ func (ec *executionContext) marshalOMediaItem2ᚕᚖreichardᚗioᚋimaginiᚋgr
return ret return ret
} }
func (ec *executionContext) marshalOMediaItem2ᚕᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItemᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.MediaItem) graphql.Marshaler {
if v == nil {
return graphql.Null
}
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.marshalNMediaItem2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItem(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalOMediaItem2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItem(ctx context.Context, sel ast.SelectionSet, v *model.MediaItem) graphql.Marshaler { func (ec *executionContext) marshalOMediaItem2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐMediaItem(ctx context.Context, sel ast.SelectionSet, v *model.MediaItem) graphql.Marshaler {
if v == nil { if v == nil {
return graphql.Null return graphql.Null

74
graph/helpers.go Normal file
View File

@ -0,0 +1,74 @@
package graph
import (
"net/http"
"strings"
"time"
"github.com/dsoprea/go-exif/v3"
exifcommon "github.com/dsoprea/go-exif/v3/common"
"reichard.io/imagini/graph/model"
)
func deriveDeviceType(r *http.Request) model.DeviceType {
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
if strings.Contains(userAgent, "ios-imagini") {
return model.DeviceTypeIOs
} else if strings.Contains(userAgent, "android-imagini") {
return model.DeviceTypeAndroid
} else if strings.Contains(userAgent, "chrome") {
return model.DeviceTypeChrome
} else if strings.Contains(userAgent, "firefox") {
return model.DeviceTypeFirefox
} else if strings.Contains(userAgent, "msie") {
return model.DeviceTypeInternetExplorer
} else if strings.Contains(userAgent, "edge") {
return model.DeviceTypeEdge
} else if strings.Contains(userAgent, "safari") {
return model.DeviceTypeSafari
}
return model.DeviceTypeUnknown
}
func mediaItemFromEXIFData(filePath string) (*model.MediaItem, error) {
rawExif, err := exif.SearchFileAndExtractExif(filePath)
entries, _, err := exif.GetFlatExifData(rawExif, nil)
decLong := float64(1)
decLat := float64(1)
mediaItem := &model.MediaItem{}
for _, v := range entries {
if v.TagName == "DateTimeOriginal" {
formattedTime, _ := time.Parse("2006:01:02 15:04:05", v.Formatted)
mediaItem.ExifDate = &formattedTime
} else if v.TagName == "GPSLatitude" {
latStruct := v.Value.([]exifcommon.Rational)
decLat *= deriveDecimalCoordinate(
latStruct[0].Numerator/latStruct[0].Denominator,
latStruct[1].Numerator/latStruct[1].Denominator,
float64(latStruct[2].Numerator)/float64(latStruct[2].Denominator),
)
} else if v.TagName == "GPSLongitude" {
longStruct := v.Value.([]exifcommon.Rational)
decLong *= deriveDecimalCoordinate(
longStruct[0].Numerator/longStruct[0].Denominator,
longStruct[1].Numerator/longStruct[1].Denominator,
float64(longStruct[2].Numerator)/float64(longStruct[2].Denominator),
)
} else if v.TagName == "GPSLatitudeRef" && v.Formatted == "S" {
decLat *= -1
} else if v.TagName == "GPSLongitudeRef" && v.Formatted == "W" {
decLong *= -1
}
}
mediaItem.Latitude = &decLat
mediaItem.Longitude = &decLong
return mediaItem, err
}
func deriveDecimalCoordinate(degrees, minutes uint32, seconds float64) float64 {
return float64(degrees) + (float64(minutes) / 60) + (seconds / 3600)
}

View File

@ -57,7 +57,6 @@ type Device struct {
Name string `json:"name" gorm:"not null"` Name string `json:"name" gorm:"not null"`
Type DeviceType `json:"type" gorm:"default:Unknown;not null"` Type DeviceType `json:"type" gorm:"default:Unknown;not null"`
UserID string `json:"userID" gorm:"not null"` UserID string `json:"userID" gorm:"not null"`
User *User `json:"user" gorm:"foreignKey:ID;references:UserID;not null"`
RefreshKey *string `json:"refreshKey" ` RefreshKey *string `json:"refreshKey" `
} }
@ -125,7 +124,6 @@ type MediaItem struct {
Tags []*Tag `json:"tags" gorm:"many2many:media_tags"` Tags []*Tag `json:"tags" gorm:"many2many:media_tags"`
Albums []*Album `json:"albums" gorm:"many2many:media_albums"` Albums []*Album `json:"albums" gorm:"many2many:media_albums"`
UserID string `json:"userID" gorm:"not null"` UserID string `json:"userID" gorm:"not null"`
User *User `json:"user" gorm:"foreignKey:ID;references:UserID;not null"`
} }
type MediaItemFilter struct { type MediaItemFilter struct {
@ -152,10 +150,6 @@ type NewAlbum struct {
Name string `json:"name" ` Name string `json:"name" `
} }
type NewDevice struct {
Name string `json:"name" `
}
type NewMediaItem struct { type NewMediaItem struct {
File graphql.Upload `json:"file" ` File graphql.Upload `json:"file" `
Tags []string `json:"tags" ` Tags []string `json:"tags" `
@ -239,16 +233,18 @@ type TimeFilter struct {
} }
type User struct { type User struct {
ID string `json:"id" gorm:"primaryKey;not null"` ID string `json:"id" gorm:"primaryKey;not null"`
CreatedAt *time.Time `json:"createdAt" ` CreatedAt *time.Time `json:"createdAt" `
UpdatedAt *time.Time `json:"updatedAt" ` UpdatedAt *time.Time `json:"updatedAt" `
Email string `json:"email" gorm:"not null;unique"` Email string `json:"email" gorm:"not null;unique"`
Username string `json:"username" gorm:"not null;unique"` Username string `json:"username" gorm:"not null;unique"`
FirstName *string `json:"firstName" ` FirstName *string `json:"firstName" `
LastName *string `json:"lastName" ` LastName *string `json:"lastName" `
Role Role `json:"role" gorm:"default:User;not null"` Role Role `json:"role" gorm:"default:User;not null"`
AuthType AuthType `json:"authType" gorm:"default:Local;not null"` AuthType AuthType `json:"authType" gorm:"default:Local;not null"`
Password *string `json:"password" ` Password *string `json:"password" `
Devices []*Device `json:"devices" gorm:"foreignKey:UserID"`
MediaItems []*MediaItem `json:"mediaItems" gorm:"foreignKey:UserID"`
} }
type UserFilter struct { type UserFilter struct {

View File

@ -2,6 +2,7 @@ package graph
import ( import (
"reichard.io/imagini/internal/auth" "reichard.io/imagini/internal/auth"
"reichard.io/imagini/internal/config"
"reichard.io/imagini/internal/db" "reichard.io/imagini/internal/db"
) )
@ -10,6 +11,7 @@ import (
// It serves as dependency injection for your app, add any dependencies you require here. // It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct { type Resolver struct {
Auth *auth.AuthManager Config *config.Config
DB *db.DBManager Auth *auth.AuthManager
DB *db.DBManager
} }

View File

@ -139,6 +139,21 @@ input AuthTypeFilter {
# -------------------- Object Definitions -------------------- # -------------------- Object Definitions --------------------
# ------------------------------------------------------------ # ------------------------------------------------------------
type User {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
email: String! @meta(gorm: "not null;unique")
username: String! @meta(gorm: "not null;unique")
firstName: String
lastName: String
role: Role! @meta(gorm: "default:User;not null")
authType: AuthType! @meta(gorm: "default:Local;not null")
password: String @isPrivate
devices: [Device!] @meta(gorm: "foreignKey:UserID")
mediaItems: [MediaItem!] @meta(gorm: "foreignKey:UserID")
}
type Device { type Device {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
@ -146,23 +161,9 @@ type Device {
name: String! @meta(gorm: "not null") name: String! @meta(gorm: "not null")
type: DeviceType! @meta(gorm: "default:Unknown;not null") type: DeviceType! @meta(gorm: "default:Unknown;not null")
userID: ID! @meta(gorm: "not null") userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
refreshKey: String @isPrivate refreshKey: String @isPrivate
} }
type User {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
email: String! @meta(gorm: "not null;unique")
username: String! @meta(gorm: "not null;unique")
firstName: String
lastName: String
role: Role! @meta(gorm: "default:User;not null")
authType: AuthType! @meta(gorm: "default:Local;not null")
password: String @isPrivate
}
type MediaItem { type MediaItem {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
@ -176,21 +177,20 @@ type MediaItem {
tags: [Tag] @meta(gorm: "many2many:media_tags") tags: [Tag] @meta(gorm: "many2many:media_tags")
albums: [Album] @meta(gorm: "many2many:media_albums") albums: [Album] @meta(gorm: "many2many:media_albums")
userID: ID! @meta(gorm: "not null") userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
} }
type Tag { type Tag {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
updatedAt: Time updatedAt: Time
name: String! @meta(gorm: "unique;not null") name: String! @meta(gorm: "unique;not null")
} }
type Album { type Album {
id: ID! @meta(gorm: "primaryKey;not null") id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time createdAt: Time
updatedAt: Time updatedAt: Time
name: String! @meta(gorm: "unique;not null") name: String! @meta(gorm: "unique;not null")
} }
# ------------------------------------------------------------ # ------------------------------------------------------------
@ -272,10 +272,6 @@ input NewUser {
password: String password: String
} }
input NewDevice {
name: String!
}
input NewMediaItem { input NewMediaItem {
file: Upload! file: Upload!
tags: [ID!] tags: [ID!]
@ -377,7 +373,6 @@ type Query {
type Mutation { type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User) createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createDevice(input: NewDevice!): Device! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User) createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasMinRole(role: User) createTag(input: NewTag!): Tag! @hasMinRole(role: User)
createUser(input: NewUser!): User! @hasMinRole(role: Admin) createUser(input: NewUser!): User! @hasMinRole(role: Admin)

View File

@ -4,22 +4,100 @@ package graph
// will be copied through when generating and any unknown code will be moved to the end. // will be copied through when generating and any unknown code will be moved to the end.
import ( import (
"bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io"
"net/http" "net/http"
"os"
"path"
"strings" "strings"
"time"
"github.com/gabriel-vasile/mimetype"
"github.com/google/uuid" "github.com/google/uuid"
"reichard.io/imagini/graph/generated" "reichard.io/imagini/graph/generated"
"reichard.io/imagini/graph/model" "reichard.io/imagini/graph/model"
) )
// Done
func (r *mutationResolver) CreateMediaItem(ctx context.Context, input model.NewMediaItem) (*model.MediaItem, error) { func (r *mutationResolver) CreateMediaItem(ctx context.Context, input model.NewMediaItem) (*model.MediaItem, error) {
panic(fmt.Errorf("not implemented")) // Get Context
} authContext := ctx.Value("auth").(*model.AuthContext)
accessToken := *authContext.AccessToken
userID, ok := accessToken.Get("sub")
if !ok {
return nil, errors.New("Upload Failed")
}
func (r *mutationResolver) CreateDevice(ctx context.Context, input model.NewDevice) (*model.Device, error) { // File header placeholder
panic(fmt.Errorf("not implemented")) fileHeader := make([]byte, 64)
// Copy headers into the buffer
if _, err := input.File.File.Read(fileHeader); err != nil {
return nil, errors.New("Upload Failed")
}
// Determine media type
fileMime := mimetype.Detect(fileHeader)
contentType := fileMime.String()
var isVideo bool
if strings.HasPrefix(contentType, "image/") {
isVideo = false
} else if strings.HasPrefix(contentType, "video/") {
isVideo = true
} else {
return nil, errors.New("Upload Failed")
}
// Derive Folder & File Path
mediaItemID := uuid.New().String()
fileName := mediaItemID + fileMime.Extension()
folderPath := path.Join("/" + r.Config.DataPath + "/media/" + userID.(string))
os.MkdirAll(folderPath, 0700)
filePath := path.Join(folderPath + "/" + fileName)
// Create File
f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return nil, errors.New("Upload Failed")
}
defer f.Close()
// Copy header to file
_, err = io.Copy(f, bytes.NewReader(fileHeader))
if err != nil {
return nil, errors.New("Upload Failed")
}
// Copy remaining file
_, err = io.Copy(f, input.File.File)
if err != nil {
return nil, errors.New("Upload Failed")
}
// Create MediaItem From EXIF Data
mediaItem, err := mediaItemFromEXIFData(filePath)
if err != nil {
return nil, errors.New("Upload Failed")
}
// Add Additional MediaItem Fields
mediaItem.ID = mediaItemID
mediaItem.UserID = userID.(string)
mediaItem.IsVideo = isVideo
mediaItem.FileName = fileName
mediaItem.OrigName = input.File.Filename
// Create MediaItem in DB
err = r.DB.CreateMediaItem(mediaItem)
if err != nil {
return nil, errors.New("Upload Failed")
}
// Success
return mediaItem, nil
} }
func (r *mutationResolver) CreateAlbum(ctx context.Context, input model.NewAlbum) (*model.Album, error) { func (r *mutationResolver) CreateAlbum(ctx context.Context, input model.NewAlbum) (*model.Album, error) {
@ -50,11 +128,17 @@ func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser)
} }
func (r *queryResolver) Login(ctx context.Context, user string, password string, deviceID *string) (*model.AuthResponse, error) { func (r *queryResolver) Login(ctx context.Context, user string, password string, deviceID *string) (*model.AuthResponse, error) {
// Set Cookie From Context // Get Context
authContext := ctx.Value("auth").(*model.AuthContext) authContext := ctx.Value("auth").(*model.AuthContext)
resp := authContext.AuthResponse resp := authContext.AuthResponse
req := authContext.AuthRequest req := authContext.AuthRequest
// Clear All Cookies By Default
accessCookie := http.Cookie{Name: "AccessToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
refreshCookie := http.Cookie{Name: "RefreshToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
http.SetCookie(*resp, &accessCookie)
http.SetCookie(*resp, &refreshCookie)
// Do Login // Do Login
foundUser, success := r.Auth.AuthenticateUser(user, password) foundUser, success := r.Auth.AuthenticateUser(user, password)
if !success { if !success {
@ -62,7 +146,7 @@ func (r *queryResolver) Login(ctx context.Context, user string, password string,
} }
// Upsert Device // Upsert Device
foundDevice := model.Device{} foundDevice := model.Device{UserID: foundUser.ID}
if deviceID != nil { if deviceID != nil {
parsedDeviceID, err := uuid.Parse(*deviceID) parsedDeviceID, err := uuid.Parse(*deviceID)
if err != nil { if err != nil {
@ -75,8 +159,6 @@ func (r *queryResolver) Login(ctx context.Context, user string, password string,
} }
} else { } else {
foundDevice.Type = deriveDeviceType(req) foundDevice.Type = deriveDeviceType(req)
foundDevice.UserID = foundUser.ID
// TODO: foundDevice.User = &foundUser
err := r.DB.CreateDevice(&foundDevice) err := r.DB.CreateDevice(&foundDevice)
if err != nil { if err != nil {
return &model.AuthResponse{Result: model.AuthResultFailure}, nil return &model.AuthResponse{Result: model.AuthResultFailure}, nil
@ -94,18 +176,25 @@ func (r *queryResolver) Login(ctx context.Context, user string, password string,
} }
// Set appropriate cookies // Set appropriate cookies
accessCookie := http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true} accessCookie = http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true}
refreshCookie := http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true} refreshCookie = http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true}
http.SetCookie(*resp, &accessCookie) http.SetCookie(*resp, &accessCookie)
http.SetCookie(*resp, &refreshCookie) http.SetCookie(*resp, &refreshCookie)
// TODO: Prob bandaid
foundDevice.User = &foundUser
return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil
} }
func (r *queryResolver) Logout(ctx context.Context) (*model.AuthResponse, error) { func (r *queryResolver) Logout(ctx context.Context) (*model.AuthResponse, error) {
// panic(fmt.Errorf("not implemented")) // Set Cookie From Context
authContext := ctx.Value("auth").(*model.AuthContext)
resp := authContext.AuthResponse
// Clear All Cookies
accessCookie := http.Cookie{Name: "AccessToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
refreshCookie := http.Cookie{Name: "RefreshToken", Path: "/", HttpOnly: true, MaxAge: -1, Expires: time.Now().Add(-100 * time.Hour)}
http.SetCookie(*resp, &accessCookie)
http.SetCookie(*resp, &refreshCookie)
return &model.AuthResponse{Result: model.AuthResultSuccess}, nil return &model.AuthResponse{Result: model.AuthResultSuccess}, nil
} }
@ -172,29 +261,3 @@ func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type mutationResolver struct{ *Resolver } type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver } type queryResolver struct{ *Resolver }
// !!! WARNING !!!
// The code below was going to be deleted when updating resolvers. It has been copied here so you have
// one last chance to move it out of harms way if you want. There are two reasons this happens:
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
// it when you're done.
// - You have helper methods in this file. Move them out to keep these resolver files clean.
func deriveDeviceType(r *http.Request) model.DeviceType {
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
if strings.Contains(userAgent, "ios-imagini") {
return model.DeviceTypeIOs
} else if strings.Contains(userAgent, "android-imagini") {
return model.DeviceTypeAndroid
} else if strings.Contains(userAgent, "chrome") {
return model.DeviceTypeChrome
} else if strings.Contains(userAgent, "firefox") {
return model.DeviceTypeFirefox
} else if strings.Contains(userAgent, "msie") {
return model.DeviceTypeInternetExplorer
} else if strings.Contains(userAgent, "edge") {
return model.DeviceTypeEdge
} else if strings.Contains(userAgent, "safari") {
return model.DeviceTypeSafari
}
return model.DeviceTypeUnknown
}

View File

@ -0,0 +1 @@
package api

View File

@ -12,8 +12,9 @@ func (api *API) registerRoutes() {
// Set up Directives // Set up Directives
graphConfig := generated.Config{ graphConfig := generated.Config{
Resolvers: &graph.Resolver{ Resolvers: &graph.Resolver{
DB: api.DB, DB: api.DB,
Auth: api.Auth, Auth: api.Auth,
Config: api.Config,
}, },
Directives: generated.DirectiveRoot{ Directives: generated.DirectiveRoot{
Meta: api.metaDirective, Meta: api.metaDirective,

View File

@ -48,9 +48,28 @@ func NewMgr(c *config.Config) *DBManager {
dbm.bootstrapDatabase() dbm.bootstrapDatabase()
} }
dbm.testFeatures()
return dbm return dbm
} }
func (dbm *DBManager) testFeatures() {
// Get Devices By UserID
// var myDevices []model.Device
// dbm.db.Debug().Where(&model.Device{UserID: "97589354-cd42-40e2-bc5e-7ba6badf89fa"}).Find(&myDevices)
// fmt.Printf("Devices: %+v\n", myDevices)
// Get User by DeviceID
// var myUser []model.User
// dbm.db.Debug().Model(&model.User{}).
// Select("users.*").
// Joins("left join devices on users.id = devices.user_id").
// Where("devices.id = ?", "4e9aa851-f25b-4330-91dc-c0f975e56fa1").
// Find(&myUser)
// fmt.Printf("User: %+v\n", myUser)
}
func (dbm *DBManager) bootstrapDatabase() { func (dbm *DBManager) bootstrapDatabase() {
log.Info("[query] Bootstrapping database.") log.Info("[query] Bootstrapping database.")