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
Type func(childComplexity int) int
UpdatedAt func(childComplexity int) int
User func(childComplexity int) int
UserID func(childComplexity int) int
}
@ -94,7 +93,6 @@ type ComplexityRoot struct {
OrigName func(childComplexity int) int
Tags func(childComplexity int) int
UpdatedAt func(childComplexity int) int
User func(childComplexity int) int
UserID func(childComplexity int) int
}
@ -105,7 +103,6 @@ type ComplexityRoot struct {
Mutation struct {
CreateAlbum func(childComplexity int, input model.NewAlbum) int
CreateDevice func(childComplexity int, input model.NewDevice) int
CreateMediaItem func(childComplexity int, input model.NewMediaItem) int
CreateTag func(childComplexity int, input model.NewTag) int
CreateUser func(childComplexity int, input model.NewUser) int
@ -146,16 +143,18 @@ type ComplexityRoot struct {
}
User struct {
AuthType func(childComplexity int) int
CreatedAt func(childComplexity int) int
Email func(childComplexity int) int
FirstName func(childComplexity int) int
ID func(childComplexity int) int
LastName func(childComplexity int) int
Password func(childComplexity int) int
Role func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Username func(childComplexity int) int
AuthType func(childComplexity int) int
CreatedAt func(childComplexity int) int
Devices func(childComplexity int) int
Email func(childComplexity int) int
FirstName func(childComplexity int) int
ID func(childComplexity int) int
LastName func(childComplexity int) int
MediaItems func(childComplexity int) int
Password func(childComplexity int) int
Role func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Username func(childComplexity int) int
}
UserResponse struct {
@ -166,7 +165,6 @@ type ComplexityRoot struct {
type MutationResolver interface {
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)
CreateTag(ctx context.Context, input model.NewTag) (*model.Tag, 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
case "Device.user":
if e.complexity.Device.User == nil {
break
}
return e.complexity.Device.User(childComplexity), true
case "Device.userID":
if e.complexity.Device.UserID == nil {
break
@ -412,13 +403,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
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":
if e.complexity.MediaItem.UserID == nil {
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
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":
if e.complexity.Mutation.CreateMediaItem == nil {
break
@ -723,6 +695,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
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":
if e.complexity.User.Email == nil {
break
@ -751,6 +730,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
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":
if e.complexity.User.Password == nil {
break
@ -998,6 +984,21 @@ input AuthTypeFilter {
# -------------------- 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 {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
@ -1005,21 +1006,7 @@ type Device {
name: String! @meta(gorm: "not null")
type: DeviceType! @meta(gorm: "default:Unknown;not null")
userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
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
refreshKey: String @isPrivate
}
type MediaItem {
@ -1035,21 +1022,20 @@ type MediaItem {
tags: [Tag] @meta(gorm: "many2many:media_tags")
albums: [Album] @meta(gorm: "many2many:media_albums")
userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
}
type Tag {
id: ID! @meta(gorm: "primaryKey;not null")
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
name: String! @meta(gorm: "unique;not null")
name: String! @meta(gorm: "unique;not null")
}
type Album {
id: ID! @meta(gorm: "primaryKey;not null")
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: 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
}
input NewDevice {
name: String!
}
input NewMediaItem {
file: Upload!
tags: [ID!]
@ -1236,7 +1218,6 @@ type Query {
type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createDevice(input: NewDevice!): Device! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasMinRole(role: User)
createUser(input: NewUser!): User! @hasMinRole(role: Admin)
@ -1294,21 +1275,6 @@ func (ec *executionContext) field_Mutation_createAlbum_args(ctx context.Context,
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) {
var err error
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)
}
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() {
if r := recover(); r != nil {
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) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.User, nil
return obj.RefreshKey, 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.IsPrivate == nil {
return nil, errors.New("directive isPrivate is not implemented")
}
if ec.directives.Meta == nil {
return nil, errors.New("directive meta is not implemented")
}
return ec.directives.Meta(ctx, obj, directive0, gorm)
return ec.directives.IsPrivate(ctx, obj, directive0)
}
tmp, err := directive1(rctx)
@ -2367,45 +2329,10 @@ func (ec *executionContext) _Device_user(ctx context.Context, field graphql.Coll
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*model.User); ok {
if data, ok := tmp.(*string); 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) _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
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
})
if err != nil {
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)
}
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) {
defer func() {
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)
}
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) {
defer func() {
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)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Password, nil
directive0 := func(rctx context.Context) (interface{}, error) {
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 {
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)
}
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) {
defer func() {
if r := recover(); r != nil {
@ -6931,26 +6865,6 @@ func (ec *executionContext) unmarshalInputNewAlbum(ctx context.Context, obj inte
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) {
var it model.NewMediaItem
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 {
invalids++
}
case "user":
out.Values[i] = ec._Device_user(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "refreshKey":
out.Values[i] = ec._Device_refreshKey(ctx, field, obj)
default:
@ -7707,11 +7616,6 @@ func (ec *executionContext) _MediaItem(ctx context.Context, sel ast.SelectionSet
if out.Values[i] == graphql.Null {
invalids++
}
case "user":
out.Values[i] = ec._MediaItem_user(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
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 {
invalids++
}
case "createDevice":
out.Values[i] = ec._Mutation_createDevice(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "createAlbum":
out.Values[i] = ec._Mutation_createAlbum(ctx, field)
if out.Values[i] == graphql.Null {
@ -8163,6 +8062,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
}
case "password":
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:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -8641,11 +8544,6 @@ func (ec *executionContext) unmarshalNNewAlbum2reichardᚗioᚋimaginiᚋgraph
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) {
res, err := ec.unmarshalInputNewMediaItem(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
@ -9211,6 +9109,46 @@ func (ec *executionContext) marshalODevice2ᚕᚖreichardᚗioᚋimaginiᚋgraph
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 {
if v == nil {
return graphql.Null
@ -9523,6 +9461,46 @@ func (ec *executionContext) marshalOMediaItem2ᚕᚖreichardᚗioᚋimaginiᚋgr
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 {
if v == nil {
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"`
Type DeviceType `json:"type" gorm:"default:Unknown;not null"`
UserID string `json:"userID" gorm:"not null"`
User *User `json:"user" gorm:"foreignKey:ID;references:UserID;not null"`
RefreshKey *string `json:"refreshKey" `
}
@ -125,7 +124,6 @@ type MediaItem struct {
Tags []*Tag `json:"tags" gorm:"many2many:media_tags"`
Albums []*Album `json:"albums" gorm:"many2many:media_albums"`
UserID string `json:"userID" gorm:"not null"`
User *User `json:"user" gorm:"foreignKey:ID;references:UserID;not null"`
}
type MediaItemFilter struct {
@ -152,10 +150,6 @@ type NewAlbum struct {
Name string `json:"name" `
}
type NewDevice struct {
Name string `json:"name" `
}
type NewMediaItem struct {
File graphql.Upload `json:"file" `
Tags []string `json:"tags" `
@ -239,16 +233,18 @@ type TimeFilter struct {
}
type User struct {
ID string `json:"id" gorm:"primaryKey;not null"`
CreatedAt *time.Time `json:"createdAt" `
UpdatedAt *time.Time `json:"updatedAt" `
Email string `json:"email" gorm:"not null;unique"`
Username string `json:"username" gorm:"not null;unique"`
FirstName *string `json:"firstName" `
LastName *string `json:"lastName" `
Role Role `json:"role" gorm:"default:User;not null"`
AuthType AuthType `json:"authType" gorm:"default:Local;not null"`
Password *string `json:"password" `
ID string `json:"id" gorm:"primaryKey;not null"`
CreatedAt *time.Time `json:"createdAt" `
UpdatedAt *time.Time `json:"updatedAt" `
Email string `json:"email" gorm:"not null;unique"`
Username string `json:"username" gorm:"not null;unique"`
FirstName *string `json:"firstName" `
LastName *string `json:"lastName" `
Role Role `json:"role" gorm:"default:User;not null"`
AuthType AuthType `json:"authType" gorm:"default:Local;not null"`
Password *string `json:"password" `
Devices []*Device `json:"devices" gorm:"foreignKey:UserID"`
MediaItems []*MediaItem `json:"mediaItems" gorm:"foreignKey:UserID"`
}
type UserFilter struct {

View File

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

View File

@ -139,6 +139,21 @@ input AuthTypeFilter {
# -------------------- 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 {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
@ -146,23 +161,9 @@ type Device {
name: String! @meta(gorm: "not null")
type: DeviceType! @meta(gorm: "default:Unknown;not null")
userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
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 {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
@ -176,21 +177,20 @@ type MediaItem {
tags: [Tag] @meta(gorm: "many2many:media_tags")
albums: [Album] @meta(gorm: "many2many:media_albums")
userID: ID! @meta(gorm: "not null")
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
}
type Tag {
id: ID! @meta(gorm: "primaryKey;not null")
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
name: String! @meta(gorm: "unique;not null")
name: String! @meta(gorm: "unique;not null")
}
type Album {
id: ID! @meta(gorm: "primaryKey;not null")
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: 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
}
input NewDevice {
name: String!
}
input NewMediaItem {
file: Upload!
tags: [ID!]
@ -377,7 +373,6 @@ type Query {
type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createDevice(input: NewDevice!): Device! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasMinRole(role: User)
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.
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/gabriel-vasile/mimetype"
"github.com/google/uuid"
"reichard.io/imagini/graph/generated"
"reichard.io/imagini/graph/model"
)
// Done
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) {
panic(fmt.Errorf("not implemented"))
// File header placeholder
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) {
@ -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) {
// Set Cookie From Context
// Get Context
authContext := ctx.Value("auth").(*model.AuthContext)
resp := authContext.AuthResponse
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
foundUser, success := r.Auth.AuthenticateUser(user, password)
if !success {
@ -62,7 +146,7 @@ func (r *queryResolver) Login(ctx context.Context, user string, password string,
}
// Upsert Device
foundDevice := model.Device{}
foundDevice := model.Device{UserID: foundUser.ID}
if deviceID != nil {
parsedDeviceID, err := uuid.Parse(*deviceID)
if err != nil {
@ -75,8 +159,6 @@ func (r *queryResolver) Login(ctx context.Context, user string, password string,
}
} else {
foundDevice.Type = deriveDeviceType(req)
foundDevice.UserID = foundUser.ID
// TODO: foundDevice.User = &foundUser
err := r.DB.CreateDevice(&foundDevice)
if err != 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
accessCookie := http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true}
refreshCookie := http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true}
accessCookie = http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true}
refreshCookie = http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true}
http.SetCookie(*resp, &accessCookie)
http.SetCookie(*resp, &refreshCookie)
// TODO: Prob bandaid
foundDevice.User = &foundUser
return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil
}
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
}
@ -172,29 +261,3 @@ func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type mutationResolver 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
graphConfig := generated.Config{
Resolvers: &graph.Resolver{
DB: api.DB,
Auth: api.Auth,
DB: api.DB,
Auth: api.Auth,
Config: api.Config,
},
Directives: generated.DirectiveRoot{
Meta: api.metaDirective,

View File

@ -48,9 +48,28 @@ func NewMgr(c *config.Config) *DBManager {
dbm.bootstrapDatabase()
}
dbm.testFeatures()
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() {
log.Info("[query] Bootstrapping database.")