Enable GraphQL! #1
@ -3,14 +3,15 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"reichard.io/imagini/cmd/server"
|
"reichard.io/imagini/cmd/server"
|
||||||
|
|
||||||
"reichard.io/imagini/plugin"
|
|
||||||
"github.com/99designs/gqlgen/api"
|
"github.com/99designs/gqlgen/api"
|
||||||
"github.com/99designs/gqlgen/codegen/config"
|
"github.com/99designs/gqlgen/codegen/config"
|
||||||
|
"reichard.io/imagini/plugin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UTCFormatter struct {
|
type UTCFormatter struct {
|
||||||
|
@ -60,6 +60,7 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AuthResponse struct {
|
AuthResponse struct {
|
||||||
|
Device func(childComplexity int) int
|
||||||
Error func(childComplexity int) int
|
Error func(childComplexity int) int
|
||||||
Result func(childComplexity int) int
|
Result func(childComplexity int) int
|
||||||
}
|
}
|
||||||
@ -72,6 +73,7 @@ type ComplexityRoot struct {
|
|||||||
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
|
User func(childComplexity int) int
|
||||||
|
UserID func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceResponse struct {
|
DeviceResponse struct {
|
||||||
@ -92,6 +94,7 @@ type ComplexityRoot struct {
|
|||||||
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
|
User func(childComplexity int) int
|
||||||
|
UserID func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaItemResponse struct {
|
MediaItemResponse struct {
|
||||||
@ -118,7 +121,7 @@ type ComplexityRoot struct {
|
|||||||
Albums func(childComplexity int, filter *model.AlbumFilter, count *int, page *int) int
|
Albums func(childComplexity int, filter *model.AlbumFilter, count *int, page *int) int
|
||||||
Device func(childComplexity int, id string) int
|
Device func(childComplexity int, id string) int
|
||||||
Devices func(childComplexity int, filter *model.DeviceFilter, count *int, page *int) int
|
Devices func(childComplexity int, filter *model.DeviceFilter, count *int, page *int) int
|
||||||
Login func(childComplexity int, user string, password string) int
|
Login func(childComplexity int, user string, password string, deviceID *string) int
|
||||||
Logout func(childComplexity int) int
|
Logout func(childComplexity int) int
|
||||||
Me func(childComplexity int) int
|
Me func(childComplexity int) int
|
||||||
MediaItem func(childComplexity int, id string) int
|
MediaItem func(childComplexity int, id string) int
|
||||||
@ -168,8 +171,8 @@ type MutationResolver interface {
|
|||||||
CreateUser(ctx context.Context, input model.NewUser) (*model.User, error)
|
CreateUser(ctx context.Context, input model.NewUser) (*model.User, error)
|
||||||
}
|
}
|
||||||
type QueryResolver interface {
|
type QueryResolver interface {
|
||||||
Login(ctx context.Context, user string, password string) (model.AuthResult, error)
|
Login(ctx context.Context, user string, password string, deviceID *string) (*model.AuthResponse, error)
|
||||||
Logout(ctx context.Context) (model.AuthResult, error)
|
Logout(ctx context.Context) (*model.AuthResponse, error)
|
||||||
MediaItem(ctx context.Context, id string) (*model.MediaItem, error)
|
MediaItem(ctx context.Context, id string) (*model.MediaItem, error)
|
||||||
Device(ctx context.Context, id string) (*model.Device, error)
|
Device(ctx context.Context, id string) (*model.Device, error)
|
||||||
Album(ctx context.Context, id string) (*model.Album, error)
|
Album(ctx context.Context, id string) (*model.Album, error)
|
||||||
@ -240,6 +243,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.AlbumResponse.PageInfo(childComplexity), true
|
return e.complexity.AlbumResponse.PageInfo(childComplexity), true
|
||||||
|
|
||||||
|
case "AuthResponse.Device":
|
||||||
|
if e.complexity.AuthResponse.Device == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.AuthResponse.Device(childComplexity), true
|
||||||
|
|
||||||
case "AuthResponse.Error":
|
case "AuthResponse.Error":
|
||||||
if e.complexity.AuthResponse.Error == nil {
|
if e.complexity.AuthResponse.Error == nil {
|
||||||
break
|
break
|
||||||
@ -303,6 +313,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Device.User(childComplexity), true
|
return e.complexity.Device.User(childComplexity), true
|
||||||
|
|
||||||
|
case "Device.userID":
|
||||||
|
if e.complexity.Device.UserID == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Device.UserID(childComplexity), true
|
||||||
|
|
||||||
case "DeviceResponse.data":
|
case "DeviceResponse.data":
|
||||||
if e.complexity.DeviceResponse.Data == nil {
|
if e.complexity.DeviceResponse.Data == nil {
|
||||||
break
|
break
|
||||||
@ -401,6 +418,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.MediaItem.User(childComplexity), true
|
return e.complexity.MediaItem.User(childComplexity), true
|
||||||
|
|
||||||
|
case "MediaItem.userID":
|
||||||
|
if e.complexity.MediaItem.UserID == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.MediaItem.UserID(childComplexity), true
|
||||||
|
|
||||||
case "MediaItemResponse.data":
|
case "MediaItemResponse.data":
|
||||||
if e.complexity.MediaItemResponse.Data == nil {
|
if e.complexity.MediaItemResponse.Data == nil {
|
||||||
break
|
break
|
||||||
@ -554,7 +578,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Query.Login(childComplexity, args["user"].(string), args["password"].(string)), true
|
return e.complexity.Query.Login(childComplexity, args["user"].(string), args["password"].(string), args["deviceID"].(*string)), true
|
||||||
|
|
||||||
case "Query.logout":
|
case "Query.logout":
|
||||||
if e.complexity.Query.Logout == nil {
|
if e.complexity.Query.Logout == nil {
|
||||||
@ -876,6 +900,7 @@ enum AuthResult {
|
|||||||
|
|
||||||
type AuthResponse {
|
type AuthResponse {
|
||||||
Result: AuthResult!
|
Result: AuthResult!
|
||||||
|
Device: Device
|
||||||
Error: String
|
Error: String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,17 +997,18 @@ input AuthTypeFilter {
|
|||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
type Device {
|
type Device {
|
||||||
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: "not null")
|
name: String! @meta(gorm: "not null")
|
||||||
type: DeviceType! @meta(gorm: "default:Unknown;not null")
|
type: DeviceType! @meta(gorm: "default:Unknown;not null")
|
||||||
user: User @meta(gorm: "ForeignKey:ID;not null")
|
userID: ID! @meta(gorm: "not null")
|
||||||
|
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
|
||||||
refreshKey: String
|
refreshKey: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
id: ID @meta(gorm: "primarykey;not null")
|
id: ID! @meta(gorm: "primaryKey;not null")
|
||||||
createdAt: Time
|
createdAt: Time
|
||||||
updatedAt: Time
|
updatedAt: Time
|
||||||
email: String! @meta(gorm: "not null;unique")
|
email: String! @meta(gorm: "not null;unique")
|
||||||
@ -995,7 +1021,7 @@ type User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MediaItem {
|
type MediaItem {
|
||||||
id: ID @meta(gorm: "primarykey;not null")
|
id: ID! @meta(gorm: "primaryKey;not null")
|
||||||
createdAt: Time
|
createdAt: Time
|
||||||
updatedAt: Time
|
updatedAt: Time
|
||||||
exifDate: Time
|
exifDate: Time
|
||||||
@ -1006,18 +1032,19 @@ type MediaItem {
|
|||||||
origName: String! @meta(gorm: "not null")
|
origName: String! @meta(gorm: "not null")
|
||||||
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")
|
||||||
user: User @meta(gorm: "ForeignKey:ID;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")
|
||||||
@ -1165,8 +1192,9 @@ type Query {
|
|||||||
login(
|
login(
|
||||||
user: String!
|
user: String!
|
||||||
password: String!
|
password: String!
|
||||||
): AuthResult!
|
deviceID: ID
|
||||||
logout: AuthResult! @hasMinRole(role: User)
|
): AuthResponse!
|
||||||
|
logout: AuthResponse! @hasMinRole(role: User)
|
||||||
|
|
||||||
# Single Item
|
# Single Item
|
||||||
mediaItem(id: ID!): MediaItem! @hasMinRole(role: User)
|
mediaItem(id: ID!): MediaItem! @hasMinRole(role: User)
|
||||||
@ -1456,6 +1484,15 @@ func (ec *executionContext) field_Query_login_args(ctx context.Context, rawArgs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
args["password"] = arg1
|
args["password"] = arg1
|
||||||
|
var arg2 *string
|
||||||
|
if tmp, ok := rawArgs["deviceID"]; ok {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("deviceID"))
|
||||||
|
arg2, err = ec.unmarshalOID2ᚖstring(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["deviceID"] = arg2
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1663,7 +1700,7 @@ func (ec *executionContext) _Album_id(ctx context.Context, field graphql.Collect
|
|||||||
return obj.ID, nil
|
return obj.ID, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primarykey;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primaryKey;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1680,21 +1717,24 @@ func (ec *executionContext) _Album_id(ctx context.Context, field graphql.Collect
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(*string); ok {
|
if data, ok := tmp.(string); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*string)
|
res := resTmp.(string)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOID2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalNID2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Album_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.Album) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Album_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.Album) (ret graphql.Marshaler) {
|
||||||
@ -1922,6 +1962,38 @@ func (ec *executionContext) _AuthResponse_Result(ctx context.Context, field grap
|
|||||||
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
|
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _AuthResponse_Device(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "AuthResponse",
|
||||||
|
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.Device, nil
|
||||||
|
})
|
||||||
|
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) _AuthResponse_Error(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
func (ec *executionContext) _AuthResponse_Error(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -1976,7 +2048,7 @@ func (ec *executionContext) _Device_id(ctx context.Context, field graphql.Collec
|
|||||||
return obj.ID, nil
|
return obj.ID, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primarykey;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primaryKey;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1993,21 +2065,24 @@ func (ec *executionContext) _Device_id(ctx context.Context, field graphql.Collec
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(*string); ok {
|
if data, ok := tmp.(string); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*string)
|
res := resTmp.(string)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOID2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalNID2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Device_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Device_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) {
|
||||||
@ -2192,6 +2267,65 @@ func (ec *executionContext) _Device_type(ctx context.Context, field graphql.Coll
|
|||||||
return ec.marshalNDeviceType2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐDeviceType(ctx, field.Selections, res)
|
return ec.marshalNDeviceType2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐDeviceType(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Device_userID(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) {
|
||||||
|
directive0 := func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.UserID, nil
|
||||||
|
}
|
||||||
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "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.(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)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(string)
|
||||||
|
fc.Result = 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_user(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 {
|
||||||
@ -2214,7 +2348,7 @@ func (ec *executionContext) _Device_user(ctx context.Context, field graphql.Coll
|
|||||||
return obj.User, nil
|
return obj.User, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "ForeignKey:ID;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "foreignKey:ID;references:UserID;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2241,11 +2375,14 @@ func (ec *executionContext) _Device_user(ctx context.Context, field graphql.Coll
|
|||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*model.User)
|
res := resTmp.(*model.User)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOUser2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐUser(ctx, field.Selections, 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) {
|
func (ec *executionContext) _Device_refreshKey(ctx context.Context, field graphql.CollectedField, obj *model.Device) (ret graphql.Marshaler) {
|
||||||
@ -2369,7 +2506,7 @@ func (ec *executionContext) _MediaItem_id(ctx context.Context, field graphql.Col
|
|||||||
return obj.ID, nil
|
return obj.ID, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primarykey;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primaryKey;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2386,21 +2523,24 @@ func (ec *executionContext) _MediaItem_id(ctx context.Context, field graphql.Col
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(*string); ok {
|
if data, ok := tmp.(string); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*string)
|
res := resTmp.(string)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOID2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalNID2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _MediaItem_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.MediaItem) (ret graphql.Marshaler) {
|
func (ec *executionContext) _MediaItem_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.MediaItem) (ret graphql.Marshaler) {
|
||||||
@ -2852,6 +2992,65 @@ func (ec *executionContext) _MediaItem_albums(ctx context.Context, field graphql
|
|||||||
return ec.marshalOAlbum2ᚕᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAlbum(ctx, field.Selections, res)
|
return ec.marshalOAlbum2ᚕᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAlbum(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _MediaItem_userID(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.UserID, nil
|
||||||
|
}
|
||||||
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "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.(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)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(string)
|
||||||
|
fc.Result = 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) {
|
func (ec *executionContext) _MediaItem_user(ctx context.Context, field graphql.CollectedField, obj *model.MediaItem) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -2874,7 +3073,7 @@ func (ec *executionContext) _MediaItem_user(ctx context.Context, field graphql.C
|
|||||||
return obj.User, nil
|
return obj.User, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "ForeignKey:ID;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "foreignKey:ID;references:UserID;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2901,11 +3100,14 @@ func (ec *executionContext) _MediaItem_user(ctx context.Context, field graphql.C
|
|||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*model.User)
|
res := resTmp.(*model.User)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOUser2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐUser(ctx, field.Selections, 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) {
|
||||||
@ -3435,7 +3637,7 @@ func (ec *executionContext) _Query_login(ctx context.Context, field graphql.Coll
|
|||||||
fc.Args = args
|
fc.Args = args
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Query().Login(rctx, args["user"].(string), args["password"].(string))
|
return ec.resolvers.Query().Login(rctx, args["user"].(string), args["password"].(string), args["deviceID"].(*string))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -3447,9 +3649,9 @@ func (ec *executionContext) _Query_login(ctx context.Context, field graphql.Coll
|
|||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(model.AuthResult)
|
res := resTmp.(*model.AuthResponse)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
|
return ec.marshalNAuthResponse2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Query_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Query_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
@ -3491,10 +3693,10 @@ func (ec *executionContext) _Query_logout(ctx context.Context, field graphql.Col
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(model.AuthResult); ok {
|
if data, ok := tmp.(*model.AuthResponse); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be reichard.io/imagini/graph/model.AuthResult`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be *reichard.io/imagini/graph/model.AuthResponse`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
@ -3506,9 +3708,9 @@ func (ec *executionContext) _Query_logout(ctx context.Context, field graphql.Col
|
|||||||
}
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(model.AuthResult)
|
res := resTmp.(*model.AuthResponse)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx, field.Selections, res)
|
return ec.marshalNAuthResponse2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Query_mediaItem(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Query_mediaItem(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
@ -4323,7 +4525,7 @@ func (ec *executionContext) _Tag_id(ctx context.Context, field graphql.Collected
|
|||||||
return obj.ID, nil
|
return obj.ID, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primarykey;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primaryKey;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -4340,21 +4542,24 @@ func (ec *executionContext) _Tag_id(ctx context.Context, field graphql.Collected
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(*string); ok {
|
if data, ok := tmp.(string); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*string)
|
res := resTmp.(string)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOID2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalNID2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Tag_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.Tag) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Tag_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.Tag) (ret graphql.Marshaler) {
|
||||||
@ -4569,7 +4774,7 @@ func (ec *executionContext) _User_id(ctx context.Context, field graphql.Collecte
|
|||||||
return obj.ID, nil
|
return obj.ID, nil
|
||||||
}
|
}
|
||||||
directive1 := func(ctx context.Context) (interface{}, error) {
|
directive1 := func(ctx context.Context) (interface{}, error) {
|
||||||
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primarykey;not null")
|
gorm, err := ec.unmarshalOString2ᚖstring(ctx, "primaryKey;not null")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -4586,21 +4791,24 @@ func (ec *executionContext) _User_id(ctx context.Context, field graphql.Collecte
|
|||||||
if tmp == nil {
|
if tmp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if data, ok := tmp.(*string); ok {
|
if data, ok := tmp.(string); ok {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
|
return nil, fmt.Errorf(`unexpected type %T from directive, should be string`, tmp)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.(*string)
|
res := resTmp.(string)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOID2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalNID2string(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _User_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
|
func (ec *executionContext) _User_createdAt(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
|
||||||
@ -7282,6 +7490,9 @@ func (ec *executionContext) _Album(ctx context.Context, sel ast.SelectionSet, ob
|
|||||||
out.Values[i] = graphql.MarshalString("Album")
|
out.Values[i] = graphql.MarshalString("Album")
|
||||||
case "id":
|
case "id":
|
||||||
out.Values[i] = ec._Album_id(ctx, field, obj)
|
out.Values[i] = ec._Album_id(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._Album_createdAt(ctx, field, obj)
|
out.Values[i] = ec._Album_createdAt(ctx, field, obj)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@ -7347,6 +7558,8 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "Device":
|
||||||
|
out.Values[i] = ec._AuthResponse_Device(ctx, field, obj)
|
||||||
case "Error":
|
case "Error":
|
||||||
out.Values[i] = ec._AuthResponse_Error(ctx, field, obj)
|
out.Values[i] = ec._AuthResponse_Error(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
@ -7373,6 +7586,9 @@ func (ec *executionContext) _Device(ctx context.Context, sel ast.SelectionSet, o
|
|||||||
out.Values[i] = graphql.MarshalString("Device")
|
out.Values[i] = graphql.MarshalString("Device")
|
||||||
case "id":
|
case "id":
|
||||||
out.Values[i] = ec._Device_id(ctx, field, obj)
|
out.Values[i] = ec._Device_id(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._Device_createdAt(ctx, field, obj)
|
out.Values[i] = ec._Device_createdAt(ctx, field, obj)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@ -7387,8 +7603,16 @@ 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 "userID":
|
||||||
|
out.Values[i] = ec._Device_userID(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "user":
|
case "user":
|
||||||
out.Values[i] = ec._Device_user(ctx, field, obj)
|
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:
|
||||||
@ -7444,6 +7668,9 @@ func (ec *executionContext) _MediaItem(ctx context.Context, sel ast.SelectionSet
|
|||||||
out.Values[i] = graphql.MarshalString("MediaItem")
|
out.Values[i] = graphql.MarshalString("MediaItem")
|
||||||
case "id":
|
case "id":
|
||||||
out.Values[i] = ec._MediaItem_id(ctx, field, obj)
|
out.Values[i] = ec._MediaItem_id(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._MediaItem_createdAt(ctx, field, obj)
|
out.Values[i] = ec._MediaItem_createdAt(ctx, field, obj)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@ -7473,8 +7700,16 @@ func (ec *executionContext) _MediaItem(ctx context.Context, sel ast.SelectionSet
|
|||||||
out.Values[i] = ec._MediaItem_tags(ctx, field, obj)
|
out.Values[i] = ec._MediaItem_tags(ctx, field, obj)
|
||||||
case "albums":
|
case "albums":
|
||||||
out.Values[i] = ec._MediaItem_albums(ctx, field, obj)
|
out.Values[i] = ec._MediaItem_albums(ctx, field, obj)
|
||||||
|
case "userID":
|
||||||
|
out.Values[i] = ec._MediaItem_userID(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "user":
|
case "user":
|
||||||
out.Values[i] = ec._MediaItem_user(ctx, field, obj)
|
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))
|
||||||
}
|
}
|
||||||
@ -7828,6 +8063,9 @@ func (ec *executionContext) _Tag(ctx context.Context, sel ast.SelectionSet, obj
|
|||||||
out.Values[i] = graphql.MarshalString("Tag")
|
out.Values[i] = graphql.MarshalString("Tag")
|
||||||
case "id":
|
case "id":
|
||||||
out.Values[i] = ec._Tag_id(ctx, field, obj)
|
out.Values[i] = ec._Tag_id(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._Tag_createdAt(ctx, field, obj)
|
out.Values[i] = ec._Tag_createdAt(ctx, field, obj)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@ -7890,6 +8128,9 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
|
|||||||
out.Values[i] = graphql.MarshalString("User")
|
out.Values[i] = graphql.MarshalString("User")
|
||||||
case "id":
|
case "id":
|
||||||
out.Values[i] = ec._User_id(ctx, field, obj)
|
out.Values[i] = ec._User_id(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "createdAt":
|
case "createdAt":
|
||||||
out.Values[i] = ec._User_createdAt(ctx, field, obj)
|
out.Values[i] = ec._User_createdAt(ctx, field, obj)
|
||||||
case "updatedAt":
|
case "updatedAt":
|
||||||
@ -8233,6 +8474,20 @@ func (ec *executionContext) marshalNAlbumResponse2ᚖreichardᚗioᚋimaginiᚋg
|
|||||||
return ec._AlbumResponse(ctx, sel, v)
|
return ec._AlbumResponse(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNAuthResponse2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v model.AuthResponse) graphql.Marshaler {
|
||||||
|
return ec._AuthResponse(ctx, sel, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNAuthResponse2ᚖreichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v *model.AuthResponse) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return ec._AuthResponse(ctx, sel, v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx context.Context, v interface{}) (model.AuthResult, error) {
|
func (ec *executionContext) unmarshalNAuthResult2reichardᚗioᚋimaginiᚋgraphᚋmodelᚐAuthResult(ctx context.Context, v interface{}) (model.AuthResult, error) {
|
||||||
var res model.AuthResult
|
var res model.AuthResult
|
||||||
err := res.UnmarshalGQL(v)
|
err := res.UnmarshalGQL(v)
|
||||||
|
@ -2,11 +2,12 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/lestrrat-go/jwx/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthContext struct {
|
type AuthContext struct {
|
||||||
AccessToken string
|
AccessToken *jwt.Token
|
||||||
RefreshToken string
|
|
||||||
AuthResponse *http.ResponseWriter
|
AuthResponse *http.ResponseWriter
|
||||||
AuthRequest *http.Request
|
AuthRequest *http.Request
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gorm.io/gorm"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
|
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
newID := uuid.New().String()
|
newID := uuid.New().String()
|
||||||
u.ID = &newID
|
u.ID = newID
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Album) BeforeCreate(tx *gorm.DB) (err error) {
|
func (a *Album) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
newID := uuid.New().String()
|
newID := uuid.New().String()
|
||||||
a.ID = &newID
|
a.ID = newID
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MediaItem) BeforeCreate(tx *gorm.DB) (err error) {
|
func (m *MediaItem) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
newID := uuid.New().String()
|
newID := uuid.New().String()
|
||||||
m.ID = &newID
|
m.ID = newID
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tag) BeforeCreate(tx *gorm.DB) (err error) {
|
func (t *Tag) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
newID := uuid.New().String()
|
newID := uuid.New().String()
|
||||||
t.ID = &newID
|
t.ID = newID
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) BeforeCreate(tx *gorm.DB) (err error) {
|
func (d *Device) BeforeCreate(tx *gorm.DB) (err error) {
|
||||||
newID := uuid.New().String()
|
newID := uuid.New().String()
|
||||||
d.ID = &newID
|
d.ID = newID
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Album struct {
|
type Album 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" `
|
||||||
Name string `json:"name" gorm:"unique;not null"`
|
Name string `json:"name" gorm:"unique;not null"`
|
||||||
@ -34,6 +34,7 @@ type AlbumResponse struct {
|
|||||||
|
|
||||||
type AuthResponse struct {
|
type AuthResponse struct {
|
||||||
Result AuthResult `json:"Result" `
|
Result AuthResult `json:"Result" `
|
||||||
|
Device *Device `json:"Device" `
|
||||||
Error *string `json:"Error" `
|
Error *string `json:"Error" `
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,12 +51,13 @@ type BooleanFilter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device 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" `
|
||||||
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"`
|
||||||
User *User `json:"user" gorm:"ForeignKey:ID;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" `
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +113,7 @@ type IntFilter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MediaItem struct {
|
type MediaItem 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" `
|
||||||
ExifDate *time.Time `json:"exifDate" `
|
ExifDate *time.Time `json:"exifDate" `
|
||||||
@ -122,7 +124,8 @@ type MediaItem struct {
|
|||||||
OrigName string `json:"origName" gorm:"not null"`
|
OrigName string `json:"origName" gorm:"not null"`
|
||||||
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"`
|
||||||
User *User `json:"user" gorm:"ForeignKey:ID;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 {
|
||||||
@ -206,7 +209,7 @@ type StringFilter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Tag struct {
|
type Tag 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" `
|
||||||
Name string `json:"name" gorm:"unique;not null"`
|
Name string `json:"name" gorm:"unique;not null"`
|
||||||
@ -236,7 +239,7 @@ 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"`
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package graph
|
package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reichard.io/imagini/internal/auth"
|
||||||
"reichard.io/imagini/internal/db"
|
"reichard.io/imagini/internal/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8,6 +9,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
|
||||||
DB *db.DBManager
|
DB *db.DBManager
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ enum AuthResult {
|
|||||||
|
|
||||||
type AuthResponse {
|
type AuthResponse {
|
||||||
Result: AuthResult!
|
Result: AuthResult!
|
||||||
|
Device: Device
|
||||||
Error: String
|
Error: String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,17 +139,18 @@ input AuthTypeFilter {
|
|||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
type Device {
|
type Device {
|
||||||
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: "not null")
|
name: String! @meta(gorm: "not null")
|
||||||
type: DeviceType! @meta(gorm: "default:Unknown;not null")
|
type: DeviceType! @meta(gorm: "default:Unknown;not null")
|
||||||
user: User @meta(gorm: "ForeignKey:ID;not null")
|
userID: ID! @meta(gorm: "not null")
|
||||||
|
user: User! @meta(gorm: "foreignKey:ID;references:UserID;not null")
|
||||||
refreshKey: String
|
refreshKey: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
id: ID @meta(gorm: "primarykey;not null")
|
id: ID! @meta(gorm: "primaryKey;not null")
|
||||||
createdAt: Time
|
createdAt: Time
|
||||||
updatedAt: Time
|
updatedAt: Time
|
||||||
email: String! @meta(gorm: "not null;unique")
|
email: String! @meta(gorm: "not null;unique")
|
||||||
@ -161,7 +163,7 @@ type User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MediaItem {
|
type MediaItem {
|
||||||
id: ID @meta(gorm: "primarykey;not null")
|
id: ID! @meta(gorm: "primaryKey;not null")
|
||||||
createdAt: Time
|
createdAt: Time
|
||||||
updatedAt: Time
|
updatedAt: Time
|
||||||
exifDate: Time
|
exifDate: Time
|
||||||
@ -172,18 +174,19 @@ type MediaItem {
|
|||||||
origName: String! @meta(gorm: "not null")
|
origName: String! @meta(gorm: "not null")
|
||||||
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")
|
||||||
user: User @meta(gorm: "ForeignKey:ID;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")
|
||||||
@ -331,8 +334,9 @@ type Query {
|
|||||||
login(
|
login(
|
||||||
user: String!
|
user: String!
|
||||||
password: String!
|
password: String!
|
||||||
): AuthResult!
|
deviceID: ID
|
||||||
logout: AuthResult! @hasMinRole(role: User)
|
): AuthResponse!
|
||||||
|
logout: AuthResponse! @hasMinRole(role: User)
|
||||||
|
|
||||||
# Single Item
|
# Single Item
|
||||||
mediaItem(id: ID!): MediaItem! @hasMinRole(role: User)
|
mediaItem(id: ID!): MediaItem! @hasMinRole(role: User)
|
||||||
|
@ -4,10 +4,12 @@ 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 (
|
||||||
"net/http"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"reichard.io/imagini/graph/generated"
|
"reichard.io/imagini/graph/generated"
|
||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
@ -41,26 +43,66 @@ func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser)
|
|||||||
|
|
||||||
err := r.DB.CreateUser(user)
|
err := r.DB.CreateUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("DB Error"))
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Login(ctx context.Context, user string, password string) (model.AuthResult, error) {
|
func (r *queryResolver) Login(ctx context.Context, user string, password string, deviceID *string) (*model.AuthResponse, error) {
|
||||||
|
|
||||||
// Set Cookie From Context
|
// Set Cookie From Context
|
||||||
authContext := ctx.Value("auth").(*model.AuthContext)
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
||||||
resp := *authContext.AuthResponse
|
resp := authContext.AuthResponse
|
||||||
testCookie := http.Cookie{Name: "TestCookie", Value: "Test123", Path: "/", HttpOnly: true}
|
req := authContext.AuthRequest
|
||||||
http.SetCookie(resp, &testCookie)
|
|
||||||
|
|
||||||
return model.AuthResultSuccess, nil
|
// Do Login
|
||||||
|
foundUser, success := r.Auth.AuthenticateUser(user, password)
|
||||||
|
if !success {
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upsert Device
|
||||||
|
foundDevice := model.Device{}
|
||||||
|
if deviceID != nil {
|
||||||
|
parsedDeviceID, err := uuid.Parse(*deviceID)
|
||||||
|
if err != nil {
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
||||||
|
}
|
||||||
|
foundDevice.ID = parsedDeviceID.String()
|
||||||
|
count, err := r.DB.Device(&foundDevice)
|
||||||
|
if count != 1 || err != nil {
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foundDevice.Type = deriveDeviceType(req)
|
||||||
|
err := r.DB.CreateDevice(&foundDevice)
|
||||||
|
if err != nil {
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Tokens
|
||||||
|
accessToken, err := r.Auth.CreateJWTAccessToken(foundUser, foundDevice)
|
||||||
|
if err != nil {
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
||||||
|
}
|
||||||
|
refreshToken, err := r.Auth.CreateJWTRefreshToken(foundUser, foundDevice)
|
||||||
|
if err != nil {
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set appropriate cookies
|
||||||
|
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)
|
||||||
|
|
||||||
|
return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Logout(ctx context.Context) (model.AuthResult, error) {
|
func (r *queryResolver) Logout(ctx context.Context) (*model.AuthResponse, error) {
|
||||||
// panic(fmt.Errorf("not implemented"))
|
// panic(fmt.Errorf("not implemented"))
|
||||||
return model.AuthResultSuccess, nil
|
return &model.AuthResponse{Result: model.AuthResultSuccess}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) MediaItem(ctx context.Context, id string) (*model.MediaItem, error) {
|
func (r *queryResolver) MediaItem(ctx context.Context, id string) (*model.MediaItem, error) {
|
||||||
@ -126,3 +168,29 @@ 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
|
||||||
|
}
|
||||||
|
@ -1,248 +1,124 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
"strings"
|
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/lestrrat-go/jwx/jwt"
|
|
||||||
"github.com/99designs/gqlgen/graphql"
|
"github.com/99designs/gqlgen/graphql"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/lestrrat-go/jwx/jwt"
|
||||||
|
|
||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) loginHandler(w http.ResponseWriter, r *http.Request) {
|
func (api *API) refreshTokens(refreshToken jwt.Token) (string, string, error) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
// Acquire User & Device
|
||||||
if r.Method != http.MethodPost {
|
did, ok := refreshToken.Get("did")
|
||||||
errorJSON(w, "Method is not supported.", http.StatusMethodNotAllowed)
|
if !ok {
|
||||||
return
|
return "", "", errors.New("Missing DID")
|
||||||
}
|
}
|
||||||
|
uid, ok := refreshToken.Get(jwt.SubjectKey)
|
||||||
// Decode into Struct
|
if !ok {
|
||||||
var creds APICredentials
|
return "", "", errors.New("Missing UID")
|
||||||
err := json.NewDecoder(r.Body).Decode(&creds)
|
}
|
||||||
|
deviceUUID, err := uuid.Parse(fmt.Sprintf("%v", did))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorJSON(w, "Invalid parameters.", http.StatusBadRequest)
|
return "", "", errors.New("Invalid DID")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
userUUID, err := uuid.Parse(fmt.Sprintf("%v", uid))
|
||||||
// Validate
|
|
||||||
if creds.User == "" || creds.Password == "" {
|
|
||||||
errorJSON(w, "Invalid parameters.", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do login
|
|
||||||
resp, user := api.Auth.AuthenticateUser(creds.User, creds.Password)
|
|
||||||
if !resp {
|
|
||||||
errorJSON(w, "Invalid credentials.", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upsert device
|
|
||||||
device, err := api.upsertRequestedDevice(user, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("[api] loginHandler - Failed to upsert device: ", err)
|
return "", "", errors.New("Invalid UID")
|
||||||
errorJSON(w, "DB error. Unable to proceed.", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Tokens
|
// Device & User Skeleton
|
||||||
accessToken, err := api.Auth.CreateJWTAccessToken(user, device)
|
user := model.User{ID: userUUID.String()}
|
||||||
refreshToken, err := api.Auth.CreateJWTRefreshToken(user, device)
|
device := model.Device{ID: deviceUUID.String()}
|
||||||
|
|
||||||
// Set appropriate cookies
|
// Find User
|
||||||
accessCookie := http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true}
|
_, err = api.DB.User(&user)
|
||||||
refreshCookie := http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true}
|
|
||||||
http.SetCookie(w, &accessCookie)
|
|
||||||
http.SetCookie(w, &refreshCookie)
|
|
||||||
|
|
||||||
// Response success
|
|
||||||
successJSON(w, "Login success.", http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) logoutHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method != http.MethodPost {
|
|
||||||
http.Error(w, "Method is not supported.", http.StatusMethodNotAllowed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Reset Refresh Key
|
|
||||||
|
|
||||||
// Clear Cookies
|
|
||||||
http.SetCookie(w, &http.Cookie{Name: "AccessToken", Expires: time.Unix(0, 0)})
|
|
||||||
http.SetCookie(w, &http.Cookie{Name: "RefreshToken", Expires: time.Unix(0, 0)})
|
|
||||||
|
|
||||||
successJSON(w, "Logout success.", http.StatusOK)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will find or create the requested device based on ID and User.
|
|
||||||
**/
|
|
||||||
func (api *API) upsertRequestedDevice(user model.User, r *http.Request) (model.Device, error) {
|
|
||||||
requestedDevice := deriveRequestedDevice(r)
|
|
||||||
requestedDevice.Type = deriveDeviceType(r)
|
|
||||||
requestedDevice.User.ID = user.ID
|
|
||||||
|
|
||||||
if *requestedDevice.ID == "" {
|
|
||||||
err := api.DB.CreateDevice(&requestedDevice)
|
|
||||||
createdDevice, err := api.DB.Device(&requestedDevice)
|
|
||||||
return createdDevice, err
|
|
||||||
}
|
|
||||||
|
|
||||||
foundDevice, err := api.DB.Device(&model.Device{
|
|
||||||
ID: requestedDevice.ID,
|
|
||||||
User: &user,
|
|
||||||
})
|
|
||||||
|
|
||||||
return foundDevice, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func deriveDeviceType(r *http.Request) model.DeviceType {
|
|
||||||
userAgent := strings.ToLower(r.Header.Get("User-Agent"))
|
|
||||||
if strings.HasPrefix(userAgent, "ios-imagini"){
|
|
||||||
return model.DeviceTypeIOs
|
|
||||||
} else if strings.HasPrefix(userAgent, "android-imagini"){
|
|
||||||
return model.DeviceTypeAndroid
|
|
||||||
} else if strings.HasPrefix(userAgent, "chrome"){
|
|
||||||
return model.DeviceTypeChrome
|
|
||||||
} else if strings.HasPrefix(userAgent, "firefox"){
|
|
||||||
return model.DeviceTypeFirefox
|
|
||||||
} else if strings.HasPrefix(userAgent, "msie"){
|
|
||||||
return model.DeviceTypeInternetExplorer
|
|
||||||
} else if strings.HasPrefix(userAgent, "edge"){
|
|
||||||
return model.DeviceTypeEdge
|
|
||||||
} else if strings.HasPrefix(userAgent, "safari"){
|
|
||||||
return model.DeviceTypeSafari
|
|
||||||
}
|
|
||||||
return model.DeviceTypeUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
func deriveRequestedDevice(r *http.Request) model.Device {
|
|
||||||
deviceSkeleton := model.Device{}
|
|
||||||
authHeader := r.Header.Get("X-Imagini-Authorization")
|
|
||||||
splitAuthInfo := strings.Split(authHeader, ",")
|
|
||||||
|
|
||||||
// For each Key - Value pair
|
|
||||||
for i := range splitAuthInfo {
|
|
||||||
|
|
||||||
// Split Key - Value
|
|
||||||
item := strings.TrimSpace(splitAuthInfo[i])
|
|
||||||
splitItem := strings.SplitN(item, "=", 2)
|
|
||||||
if len(splitItem) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive Key
|
|
||||||
key := strings.ToLower(strings.TrimSpace(splitItem[0]))
|
|
||||||
if key != "deviceid" && key != "devicename" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive Value
|
|
||||||
val := trimQuotes(strings.TrimSpace(splitItem[1]))
|
|
||||||
if key == "deviceid" {
|
|
||||||
parsedDeviceUUID, err := uuid.Parse(val)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("[auth] deriveRequestedDevice - Unable to parse requested DeviceUUID: ", val)
|
return "", "", err
|
||||||
continue
|
|
||||||
}
|
|
||||||
stringDeviceUUID := parsedDeviceUUID.String()
|
|
||||||
deviceSkeleton.ID = &stringDeviceUUID
|
|
||||||
} else if key == "devicename" {
|
|
||||||
deviceSkeleton.Name = val
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If name not set, set to type
|
// Update Access Token
|
||||||
if deviceSkeleton.Name == "" {
|
accessTokenCookie, err := api.Auth.CreateJWTAccessToken(user, device)
|
||||||
deviceSkeleton.Name = deviceSkeleton.Type.String()
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return deviceSkeleton
|
return accessTokenCookie, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) refreshAccessToken(w http.ResponseWriter, r *http.Request) (jwt.Token, error) {
|
func (api *API) validateTokens(w *http.ResponseWriter, r *http.Request) (jwt.Token, error) {
|
||||||
refreshCookie, err := r.Cookie("RefreshToken")
|
// Validate Access Token
|
||||||
if err != nil {
|
accessCookie, _ := r.Cookie("AccessToken")
|
||||||
log.Warn("[middleware] RefreshToken not found")
|
if accessCookie != nil {
|
||||||
return nil, err
|
accessToken, err := api.Auth.ValidateJWTAccessToken(accessCookie.Value)
|
||||||
|
if err == nil {
|
||||||
|
return accessToken, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate Refresh Cookie Exists
|
||||||
|
refreshCookie, _ := r.Cookie("RefreshToken")
|
||||||
|
if refreshCookie == nil {
|
||||||
|
return nil, errors.New("Tokens Invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Refresh Token
|
// Validate Refresh Token
|
||||||
refreshToken, err := api.Auth.ValidateJWTRefreshToken(refreshCookie.Value)
|
refreshToken, err := api.Auth.ValidateJWTRefreshToken(refreshCookie.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.SetCookie(w, &http.Cookie{Name: "AccessToken", Expires: time.Unix(0, 0)})
|
return nil, errors.New("Tokens Invalid")
|
||||||
http.SetCookie(w, &http.Cookie{Name: "RefreshToken", Expires: time.Unix(0, 0)})
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acquire User & Device (Trusted)
|
// Refresh Access Token & Generate New Refresh Token
|
||||||
did, ok := refreshToken.Get("did")
|
newAccessCookie, newRefreshCookie, err := api.refreshTokens(refreshToken)
|
||||||
if !ok {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
uid, ok := refreshToken.Get(jwt.SubjectKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
deviceUUID, err := uuid.Parse(fmt.Sprintf("%v", did))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
userUUID, err := uuid.Parse(fmt.Sprintf("%v", uid))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stringUserUUID := userUUID.String()
|
// TODO: Actually Refresh Refresh Token
|
||||||
stringDeviceUUID := deviceUUID.String()
|
newRefreshCookie = refreshCookie.Value
|
||||||
|
|
||||||
// Device & User Skeleton
|
// Update Access & Refresh Cookies
|
||||||
user := model.User{ID: &stringUserUUID}
|
http.SetCookie(*w, &http.Cookie{
|
||||||
device := model.Device{ID: &stringDeviceUUID}
|
Name: "AccessToken",
|
||||||
|
Value: newAccessCookie,
|
||||||
|
})
|
||||||
|
http.SetCookie(*w, &http.Cookie{
|
||||||
|
Name: "RefreshToken",
|
||||||
|
Value: newRefreshCookie,
|
||||||
|
})
|
||||||
|
|
||||||
// Update token
|
return jwt.ParseBytes([]byte(newAccessCookie))
|
||||||
accessTokenString, err := api.Auth.CreateJWTAccessToken(user, device)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
accessCookie := http.Cookie{Name: "AccessToken", Value: accessTokenString}
|
|
||||||
http.SetCookie(w, &accessCookie)
|
|
||||||
|
|
||||||
// TODO: Update Refresh Key & Token
|
|
||||||
|
|
||||||
// Convert to jwt.Token
|
|
||||||
accessTokenBytes := []byte(accessTokenString)
|
|
||||||
accessToken, err := jwt.ParseBytes(accessTokenBytes)
|
|
||||||
|
|
||||||
return accessToken, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimQuotes(s string) string {
|
func (api *API) hasMinRoleDirective(ctx context.Context, obj interface{}, next graphql.Resolver, role model.Role) (res interface{}, err error) {
|
||||||
if len(s) >= 2 {
|
authContext := ctx.Value("auth").(*model.AuthContext)
|
||||||
if s[0] == '"' && s[len(s)-1] == '"' {
|
accessToken, err := api.validateTokens(authContext.AuthResponse, authContext.AuthRequest)
|
||||||
return s[1 : len(s)-1]
|
if err != nil {
|
||||||
|
return nil, errors.New("Access Denied")
|
||||||
}
|
}
|
||||||
|
authContext.AccessToken = &accessToken
|
||||||
|
|
||||||
|
userRole, ok := accessToken.Get("role")
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Access Denied")
|
||||||
}
|
}
|
||||||
return s
|
|
||||||
|
if userRole == model.RoleAdmin.String() {
|
||||||
|
return next(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if userRole == role.String() {
|
||||||
|
return next(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("Role Not Authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasMinRoleDirective(ctx context.Context, obj interface{}, next graphql.Resolver, role model.Role) (res interface{}, err error) {
|
func (api *API) metaDirective(ctx context.Context, obj interface{}, next graphql.Resolver, gorm *string) (res interface{}, err error) {
|
||||||
// if !getCurrentUser(ctx).HasRole(role) {
|
|
||||||
// // block calling the next resolver
|
|
||||||
// return nil, fmt.Errorf("Access denied")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// or let it pass through
|
|
||||||
return next(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func metaDirective(ctx context.Context, obj interface{}, next graphql.Resolver, gorm *string) (res interface{}, err error){
|
|
||||||
return next(ctx)
|
return next(ctx)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"net/http"
|
|
||||||
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Responsible for serving up static images / videos
|
// Responsible for serving up static images / videos
|
||||||
@ -29,13 +31,14 @@ func (api *API) mediaHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// - If both, force resize with new scale
|
// - If both, force resize with new scale
|
||||||
// - If one, scale resize proportionally
|
// - If one, scale resize proportionally
|
||||||
|
|
||||||
// Pull out UUIDs
|
// Pull out userID
|
||||||
reqInfo := r.Context().Value("uuids").(map[string]string)
|
authContext := r.Context().Value("auth").(*model.AuthContext)
|
||||||
uid := reqInfo["uid"]
|
rawUserID, _ := (*authContext.AccessToken).Get("sub")
|
||||||
|
userID := rawUserID.(string)
|
||||||
|
|
||||||
// Derive Path
|
// Derive Path
|
||||||
fileName := path.Base(r.URL.Path)
|
fileName := path.Base(r.URL.Path)
|
||||||
folderPath := path.Join("/" + api.Config.DataPath + "/media/" + uid)
|
folderPath := path.Join("/" + api.Config.DataPath + "/media/" + userID)
|
||||||
mediaPath := path.Join(folderPath + "/" + fileName)
|
mediaPath := path.Join(folderPath + "/" + fileName)
|
||||||
|
|
||||||
// Check if File Exists
|
// Check if File Exists
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,83 +23,57 @@ func multipleMiddleware(h http.HandlerFunc, m ...Middleware) http.HandlerFunc {
|
|||||||
return wrapped
|
return wrapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used for the graphQL endpoints that may require access to the
|
||||||
|
* Request and ResponseWriter variables. These are used to get / set cookies.
|
||||||
|
**/
|
||||||
func (api *API) injectContextMiddleware(next http.Handler) http.Handler {
|
func (api *API) injectContextMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Info("[middleware] Entering testMiddleware...")
|
|
||||||
authContext := &model.AuthContext{
|
authContext := &model.AuthContext{
|
||||||
AuthResponse: &w,
|
AuthResponse: &w,
|
||||||
AuthRequest: r,
|
AuthRequest: r,
|
||||||
}
|
}
|
||||||
accessCookie, err := r.Cookie("AccessToken")
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("[middleware] AccessToken not found")
|
|
||||||
} else {
|
|
||||||
authContext.AccessToken = accessCookie.Value
|
|
||||||
}
|
|
||||||
refreshCookie, err := r.Cookie("RefreshToken")
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("[middleware] RefreshToken not found")
|
|
||||||
} else {
|
|
||||||
authContext.RefreshToken = refreshCookie.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add context
|
// Add context
|
||||||
ctx := context.WithValue(r.Context(), "auth", authContext)
|
ctx := context.WithValue(r.Context(), "auth", authContext)
|
||||||
r = r.WithContext(ctx)
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
log.Info("[middleware] Exiting testMiddleware...")
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used for non graphQL endpoints that require authentication.
|
||||||
|
**/
|
||||||
func (api *API) authMiddleware(next http.Handler) http.HandlerFunc {
|
func (api *API) authMiddleware(next http.Handler) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Acquire Token
|
accessToken, err := api.validateTokens(&w, r)
|
||||||
accessCookie, err := r.Cookie("AccessToken")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("[middleware] AccessToken not found")
|
|
||||||
errorJSON(w, "Invalid token.", http.StatusUnauthorized)
|
errorJSON(w, "Invalid token.", http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate JWT Tokens
|
// Create Context
|
||||||
accessToken, err := api.Auth.ValidateJWTAccessToken(accessCookie.Value)
|
authContext := &model.AuthContext{
|
||||||
|
AccessToken: &accessToken,
|
||||||
if err != nil && err.Error() == "exp not satisfied" {
|
|
||||||
log.Info("[middleware] Refreshing AccessToken")
|
|
||||||
accessToken, err = api.refreshAccessToken(w, r)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("[middleware] Refreshing AccessToken failed: ", err)
|
|
||||||
errorJSON(w, "Invalid token.", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Info("[middleware] AccessToken Refreshed")
|
|
||||||
} else if err != nil {
|
|
||||||
log.Warn("[middleware] AccessToken failed to validate")
|
|
||||||
errorJSON(w, "Invalid token.", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
ctx := context.WithValue(r.Context(), "auth", authContext)
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
// Acquire UserID and DeviceID
|
next.ServeHTTP(w, r)
|
||||||
reqInfo := make(map[string]string)
|
|
||||||
uid, _ := accessToken.Get("sub")
|
|
||||||
did, _ := accessToken.Get("did")
|
|
||||||
reqInfo["uid"] = uid.(string)
|
|
||||||
reqInfo["did"] = did.(string)
|
|
||||||
|
|
||||||
// Add context
|
|
||||||
ctx := context.WithValue(r.Context(), "uuids", reqInfo)
|
|
||||||
sr := r.WithContext(ctx)
|
|
||||||
|
|
||||||
next.ServeHTTP(w, sr)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) logMiddleware(h http.Handler) http.Handler {
|
func (api *API) logMiddleware(h http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
log.SetOutput(os.Stdout)
|
log.SetOutput(os.Stdout)
|
||||||
log.Println(r.Method, r.URL)
|
log.Println(r.Method, r.URL)
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql/handler"
|
"github.com/99designs/gqlgen/graphql/handler"
|
||||||
"github.com/99designs/gqlgen/graphql/playground"
|
"github.com/99designs/gqlgen/graphql/playground"
|
||||||
@ -14,10 +14,17 @@ import (
|
|||||||
func (api *API) registerRoutes() {
|
func (api *API) registerRoutes() {
|
||||||
|
|
||||||
// Set up Directives
|
// Set up Directives
|
||||||
c := generated.Config{ Resolvers: &graph.Resolver{ DB: api.DB } }
|
graphConfig := generated.Config{
|
||||||
c.Directives.HasMinRole = hasMinRoleDirective
|
Resolvers: &graph.Resolver{
|
||||||
c.Directives.Meta = metaDirective
|
DB: api.DB,
|
||||||
srv := handler.NewDefaultServer(generated.NewExecutableSchema(c))
|
Auth: api.Auth,
|
||||||
|
},
|
||||||
|
Directives: generated.DirectiveRoot{
|
||||||
|
Meta: api.metaDirective,
|
||||||
|
HasMinRole: api.hasMinRoleDirective,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
srv := handler.NewDefaultServer(generated.NewExecutableSchema(graphConfig))
|
||||||
|
|
||||||
// Handle GraphQL
|
// Handle GraphQL
|
||||||
api.Router.Handle("/playground", playground.Handler("GraphQL playground", "/query"))
|
api.Router.Handle("/playground", playground.Handler("GraphQL playground", "/query"))
|
||||||
|
@ -1,57 +1,52 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
"errors"
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/lestrrat-go/jwx/jwa"
|
"github.com/lestrrat-go/jwx/jwa"
|
||||||
"github.com/lestrrat-go/jwx/jwt"
|
"github.com/lestrrat-go/jwx/jwt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
"reichard.io/imagini/internal/db"
|
|
||||||
"reichard.io/imagini/internal/config"
|
"reichard.io/imagini/internal/config"
|
||||||
"reichard.io/imagini/internal/session"
|
"reichard.io/imagini/internal/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthManager struct {
|
type AuthManager struct {
|
||||||
DB *db.DBManager
|
DB *db.DBManager
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
Session *session.SessionManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMgr(db *db.DBManager, c *config.Config) *AuthManager {
|
func NewMgr(db *db.DBManager, c *config.Config) *AuthManager {
|
||||||
session := session.NewMgr()
|
|
||||||
return &AuthManager{
|
return &AuthManager{
|
||||||
DB: db,
|
DB: db,
|
||||||
Config: c,
|
Config: c,
|
||||||
Session: session,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthManager) AuthenticateUser(user, password string) (bool, model.User) {
|
func (auth *AuthManager) AuthenticateUser(user, password string) (model.User, bool) {
|
||||||
// Search Objects
|
// Find User by Username / Email
|
||||||
userByName := &model.User{}
|
foundUser := &model.User{Username: user}
|
||||||
userByName.Username = user
|
_, err := auth.DB.User(foundUser)
|
||||||
|
|
||||||
foundUser, err := auth.DB.User(userByName)
|
// By Username
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
userByEmail := &model.User{}
|
foundUser = &model.User{Email: user}
|
||||||
userByEmail.Email = user
|
_, err = auth.DB.User(foundUser)
|
||||||
foundUser, err = auth.DB.User(userByEmail)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error Checking
|
// By Email
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
log.Warn("[auth] User not found: ", user)
|
log.Warn("[auth] User not found: ", user)
|
||||||
return false, foundUser
|
return *foundUser, false
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return false, foundUser
|
return *foundUser, false
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("[auth] Authenticating user: ", foundUser.Username)
|
log.Info("[auth] Authenticating user: ", foundUser.Username)
|
||||||
@ -59,19 +54,14 @@ func (auth *AuthManager) AuthenticateUser(user, password string) (bool, model.Us
|
|||||||
// Determine Type
|
// Determine Type
|
||||||
switch foundUser.AuthType {
|
switch foundUser.AuthType {
|
||||||
case "Local":
|
case "Local":
|
||||||
return authenticateLocalUser(foundUser, password), foundUser
|
return *foundUser, authenticateLocalUser(*foundUser, password)
|
||||||
case "LDAP":
|
case "LDAP":
|
||||||
return authenticateLDAPUser(foundUser, password), foundUser
|
return *foundUser, authenticateLDAPUser(*foundUser, password)
|
||||||
default:
|
default:
|
||||||
return false, foundUser
|
return *foundUser, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthManager) getRole(user model.User) string {
|
|
||||||
// TODO: Lookup role of user
|
|
||||||
return "User"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token, error) {
|
func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token, error) {
|
||||||
byteRefreshJWT := []byte(refreshJWT)
|
byteRefreshJWT := []byte(refreshJWT)
|
||||||
|
|
||||||
@ -85,8 +75,8 @@ func (auth *AuthManager) ValidateJWTRefreshToken(refreshJWT string) (jwt.Token,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("did does not parse")
|
return nil, errors.New("did does not parse")
|
||||||
}
|
}
|
||||||
stringDeviceID := deviceID.String()
|
device := &model.Device{ID: deviceID.String()}
|
||||||
device, err := auth.DB.Device(&model.Device{ID: &stringDeviceID})
|
_, err = auth.DB.Device(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -131,7 +121,7 @@ func (auth *AuthManager) CreateJWTRefreshToken(user model.User, device model.Dev
|
|||||||
|
|
||||||
// iOS & Android = Never Expiring Refresh Token
|
// iOS & Android = Never Expiring Refresh Token
|
||||||
if device.Type != "iOS" && device.Type != "Android" {
|
if device.Type != "iOS" && device.Type != "Android" {
|
||||||
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 24)) // 1 Day Access Key
|
t.Set(jwt.ExpirationKey, tm.Add(time.Hour*24)) // 1 Day Access Key
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Token Creation
|
// Validate Token Creation
|
||||||
@ -157,11 +147,11 @@ func (auth *AuthManager) CreateJWTAccessToken(user model.User, device model.Devi
|
|||||||
tm := time.Now()
|
tm := time.Now()
|
||||||
t := jwt.New()
|
t := jwt.New()
|
||||||
t.Set(`did`, device.ID) // Device ID
|
t.Set(`did`, device.ID) // Device ID
|
||||||
t.Set(`role`, auth.getRole(user)) // User Role (Admin / User)
|
t.Set(`role`, user.Role.String()) // User Role (Admin / User)
|
||||||
t.Set(jwt.SubjectKey, user.ID) // User ID
|
t.Set(jwt.SubjectKey, user.ID) // User ID
|
||||||
t.Set(jwt.AudienceKey, `imagini`) // App ID
|
t.Set(jwt.AudienceKey, `imagini`) // App ID
|
||||||
t.Set(jwt.IssuedAtKey, tm) // Issued At
|
t.Set(jwt.IssuedAtKey, tm) // Issued At
|
||||||
t.Set(jwt.ExpirationKey, tm.Add(time.Hour * 2)) // 2 Hour Access Key
|
t.Set(jwt.ExpirationKey, tm.Add(time.Hour*2)) // 2 Hour Access Key
|
||||||
|
|
||||||
// Validate Token Creation
|
// Validate Token Creation
|
||||||
_, err := json.MarshalIndent(t, "", " ")
|
_, err := json.MarshalIndent(t, "", " ")
|
||||||
|
@ -2,16 +2,16 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"path"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
// "gorm.io/gorm/logger"
|
// "gorm.io/gorm/logger"
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"reichard.io/imagini/internal/config"
|
|
||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
|
"reichard.io/imagini/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DBManager struct {
|
type DBManager struct {
|
||||||
@ -84,7 +84,7 @@ func (dbm *DBManager) QueryBuilder(dest interface{}, params []byte) (int64, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
err := dbm.db.Find(dest).Count(&count).Error;
|
err := dbm.db.Find(dest).Count(&count).Error
|
||||||
return count, err
|
return count, err
|
||||||
|
|
||||||
// Paging:
|
// Paging:
|
||||||
|
@ -7,25 +7,24 @@ import (
|
|||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dbm *DBManager) CreateDevice (device *model.Device) error {
|
func (dbm *DBManager) CreateDevice(device *model.Device) error {
|
||||||
log.Info("[db] Creating device: ", device.Name)
|
log.Debug("[db] Creating device: ", device.Name)
|
||||||
refreshKey := uuid.New().String()
|
refreshKey := uuid.New().String()
|
||||||
device.RefreshKey = &refreshKey
|
device.RefreshKey = &refreshKey
|
||||||
err := dbm.db.Create(&device).Error
|
err := dbm.db.Create(device).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) Device (device *model.Device) (model.Device, error) {
|
func (dbm *DBManager) Device(device *model.Device) (int64, error) {
|
||||||
var foundDevice model.Device
|
|
||||||
var count int64
|
var count int64
|
||||||
err := dbm.db.Where(&device).First(&foundDevice).Count(&count).Error
|
err := dbm.db.Where(device).First(device).Count(&count).Error
|
||||||
return foundDevice, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) DeleteDevice (user *model.Device) error {
|
func (dbm *DBManager) DeleteDevice(user *model.Device) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) UpdateRefreshToken (device *model.Device, refreshToken string) error {
|
func (dbm *DBManager) UpdateRefreshToken(device *model.Device, refreshToken string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dbm *DBManager) CreateMediaItem (mediaItem *model.MediaItem) error {
|
func (dbm *DBManager) CreateMediaItem(mediaItem *model.MediaItem) error {
|
||||||
log.Info("[db] Creating media item: ", mediaItem.FileName)
|
log.Debug("[db] Creating media item: ", mediaItem.FileName)
|
||||||
err := dbm.db.Create(&mediaItem).Error
|
err := dbm.db.Create(mediaItem).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,6 +16,6 @@ func (dbm *DBManager) MediaItems(mediaItemFilter *model.MediaItem) ([]model.Medi
|
|||||||
var mediaItems []model.MediaItem
|
var mediaItems []model.MediaItem
|
||||||
var count int64
|
var count int64
|
||||||
|
|
||||||
err := dbm.db.Where(&mediaItemFilter).Find(&mediaItems).Count(&count).Error;
|
err := dbm.db.Where(mediaItemFilter).Find(&mediaItems).Count(&count).Error
|
||||||
return mediaItems, count, err
|
return mediaItems, count, err
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"reichard.io/imagini/graph/model"
|
"reichard.io/imagini/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dbm *DBManager) CreateUser (user *model.User) error {
|
func (dbm *DBManager) CreateUser(user *model.User) error {
|
||||||
log.Info("[db] Creating user: ", user.Username)
|
log.Info("[db] Creating user: ", user.Username)
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost)
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*user.Password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -16,28 +16,26 @@ func (dbm *DBManager) CreateUser (user *model.User) error {
|
|||||||
}
|
}
|
||||||
stringHashedPassword := string(hashedPassword)
|
stringHashedPassword := string(hashedPassword)
|
||||||
user.Password = &stringHashedPassword
|
user.Password = &stringHashedPassword
|
||||||
err = dbm.db.Create(&user).Error
|
return dbm.db.Create(user).Error
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) User (user *model.User) (model.User, error) {
|
func (dbm *DBManager) User(user *model.User) (int64, error) {
|
||||||
var foundUser model.User
|
|
||||||
var count int64
|
var count int64
|
||||||
err := dbm.db.Where(&user).First(&foundUser).Count(&count).Error
|
err := dbm.db.Where(user).First(user).Count(&count).Error
|
||||||
return foundUser, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) Users () ([]*model.User, int64, error) {
|
func (dbm *DBManager) Users() ([]*model.User, int64, error) {
|
||||||
var foundUsers []*model.User
|
var foundUsers []*model.User
|
||||||
var count int64
|
var count int64
|
||||||
err := dbm.db.Find(&foundUsers).Count(&count).Error
|
err := dbm.db.Find(&foundUsers).Count(&count).Error
|
||||||
return foundUsers, count, err
|
return foundUsers, count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) DeleteUser (user model.User) error {
|
func (dbm *DBManager) DeleteUser(user model.User) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbm *DBManager) UpdatePassword (user model.User, pw string) {
|
func (dbm *DBManager) UpdatePassword(user model.User, pw string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user