mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 19:49:16 +00:00
Work towards subscriptions
- Replace chi with gorilla/mux - Write custom CORS rules - Start on notification subscriptions
This commit is contained in:
2
api/.vscode/launch.json
vendored
2
api/.vscode/launch.json
vendored
@@ -9,7 +9,7 @@
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceRoot}/server/server.go",
|
||||
"program": "${workspaceRoot}/server.go",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"env": {},
|
||||
"args": []
|
||||
|
||||
@@ -8,6 +8,7 @@ require (
|
||||
github.com/go-chi/cors v1.0.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/h2non/filetype v1.0.10
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/lib/pq v1.3.0
|
||||
|
||||
@@ -4,6 +4,7 @@ github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+s
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ=
|
||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
@@ -15,7 +16,10 @@ github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
|
||||
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
|
||||
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.1 h1:KOwqsTYZdeuMacU7CxjMNYEKeBvLbxW+psodrbcEa3A=
|
||||
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/h2non/filetype v1.0.10 h1:z+SJfnL6thYJ9kAST+6nPRXp1lMxnOVbMZHNYHMar0s=
|
||||
@@ -30,6 +34,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/nf/cr2 v0.0.0-20180623103828-4699471a17ed h1:QP63yO3XEt8tJ1DgBsNjbOyBGjy2eHy9ITK4Eisr9rg=
|
||||
github.com/nf/cr2 v0.0.0-20180623103828-4699471a17ed/go.mod h1:HazDB3gS/i//QXMMRmTAV7Ni9gAi4mDNTH2HjZ5aVgU=
|
||||
@@ -39,6 +44,7 @@ github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKw
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
@@ -51,6 +57,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -42,6 +43,7 @@ type ResolverRoot interface {
|
||||
Photo() PhotoResolver
|
||||
Query() QueryResolver
|
||||
ShareToken() ShareTokenResolver
|
||||
Subscription() SubscriptionResolver
|
||||
}
|
||||
|
||||
type DirectiveRoot struct {
|
||||
@@ -81,6 +83,16 @@ type ComplexityRoot struct {
|
||||
UpdateUser func(childComplexity int, id int, username *string, rootPath *string, admin *bool) int
|
||||
}
|
||||
|
||||
Notification struct {
|
||||
Content func(childComplexity int) int
|
||||
Header func(childComplexity int) int
|
||||
Key func(childComplexity int) int
|
||||
Negative func(childComplexity int) int
|
||||
Positive func(childComplexity int) int
|
||||
Progress func(childComplexity int) int
|
||||
Type func(childComplexity int) int
|
||||
}
|
||||
|
||||
Photo struct {
|
||||
Album func(childComplexity int) int
|
||||
Downloads func(childComplexity int) int
|
||||
@@ -149,6 +161,10 @@ type ComplexityRoot struct {
|
||||
InitialSetup func(childComplexity int) int
|
||||
}
|
||||
|
||||
Subscription struct {
|
||||
Notification func(childComplexity int) int
|
||||
}
|
||||
|
||||
User struct {
|
||||
Admin func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
@@ -203,6 +219,9 @@ type ShareTokenResolver interface {
|
||||
Album(ctx context.Context, obj *models.ShareToken) (*models.Album, error)
|
||||
Photo(ctx context.Context, obj *models.ShareToken) (*models.Photo, error)
|
||||
}
|
||||
type SubscriptionResolver interface {
|
||||
Notification(ctx context.Context) (<-chan *models.Notification, error)
|
||||
}
|
||||
|
||||
type executableSchema struct {
|
||||
resolvers ResolverRoot
|
||||
@@ -440,6 +459,55 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Mutation.UpdateUser(childComplexity, args["id"].(int), args["username"].(*string), args["rootPath"].(*string), args["admin"].(*bool)), true
|
||||
|
||||
case "Notification.content":
|
||||
if e.complexity.Notification.Content == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Content(childComplexity), true
|
||||
|
||||
case "Notification.header":
|
||||
if e.complexity.Notification.Header == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Header(childComplexity), true
|
||||
|
||||
case "Notification.key":
|
||||
if e.complexity.Notification.Key == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Key(childComplexity), true
|
||||
|
||||
case "Notification.negative":
|
||||
if e.complexity.Notification.Negative == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Negative(childComplexity), true
|
||||
|
||||
case "Notification.positive":
|
||||
if e.complexity.Notification.Positive == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Positive(childComplexity), true
|
||||
|
||||
case "Notification.progress":
|
||||
if e.complexity.Notification.Progress == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Progress(childComplexity), true
|
||||
|
||||
case "Notification.type":
|
||||
if e.complexity.Notification.Type == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Type(childComplexity), true
|
||||
|
||||
case "Photo.album":
|
||||
if e.complexity.Photo.Album == nil {
|
||||
break
|
||||
@@ -778,6 +846,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.SiteInfo.InitialSetup(childComplexity), true
|
||||
|
||||
case "Subscription.notification":
|
||||
if e.complexity.Subscription.Notification == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Subscription.Notification(childComplexity), true
|
||||
|
||||
case "User.admin":
|
||||
if e.complexity.User.Admin == nil {
|
||||
break
|
||||
@@ -845,7 +920,36 @@ func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefini
|
||||
}
|
||||
|
||||
func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response {
|
||||
return graphql.OneShot(graphql.ErrorResponse(ctx, "subscriptions are not supported"))
|
||||
ec := executionContext{graphql.GetRequestContext(ctx), e}
|
||||
|
||||
next := ec._Subscription(ctx, op.SelectionSet)
|
||||
if ec.Errors != nil {
|
||||
return graphql.OneShot(&graphql.Response{Data: []byte("null"), Errors: ec.Errors})
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
return func() *graphql.Response {
|
||||
buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {
|
||||
buf.Reset()
|
||||
data := next()
|
||||
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
data.MarshalGQL(&buf)
|
||||
return buf.Bytes()
|
||||
})
|
||||
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &graphql.Response{
|
||||
Data: buf,
|
||||
Errors: ec.Errors,
|
||||
Extensions: ec.Extensions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type executionContext struct {
|
||||
@@ -955,6 +1059,25 @@ type Mutation {
|
||||
deleteUser(id: Int!): User @isAdmin
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
notification: Notification!
|
||||
}
|
||||
|
||||
enum NotificationType {
|
||||
Message
|
||||
Progress
|
||||
}
|
||||
|
||||
type Notification {
|
||||
key: String!
|
||||
type: NotificationType!
|
||||
header: String!
|
||||
content: String!
|
||||
progress: Float
|
||||
positive: Boolean!
|
||||
negative: Boolean!
|
||||
}
|
||||
|
||||
type AuthorizeResult {
|
||||
success: Boolean!
|
||||
status: String!
|
||||
@@ -2506,6 +2629,262 @@ func (ec *executionContext) _Mutation_deleteUser(ctx context.Context, field grap
|
||||
return ec.marshalOUser2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_key(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Key, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_type(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Type, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(models.NotificationType)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNNotificationType2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐNotificationType(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_header(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Header, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_content(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Content, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_progress(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Progress, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*float64)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalOFloat2ᚖfloat64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_positive(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Positive, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_negative(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Negative, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Photo_id(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
@@ -4220,6 +4599,52 @@ func (ec *executionContext) _SiteInfo_initialSetup(ctx context.Context, field gr
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Subscription_notification(ctx context.Context, field graphql.CollectedField) (ret func() graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Subscription",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Subscription().Notification(rctx)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return nil
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !ec.HasError(rctx) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return func() graphql.Marshaler {
|
||||
res, ok := <-resTmp.(<-chan *models.Notification)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return graphql.WriterFunc(func(w io.Writer) {
|
||||
w.Write([]byte{'{'})
|
||||
graphql.MarshalString(field.Alias).MarshalGQL(w)
|
||||
w.Write([]byte{':'})
|
||||
ec.marshalNNotification2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐNotification(ctx, field.Selections, res).MarshalGQL(w)
|
||||
w.Write([]byte{'}'})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (ec *executionContext) _User_id(ctx context.Context, field graphql.CollectedField, obj *models.User) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
@@ -5789,6 +6214,60 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
return out
|
||||
}
|
||||
|
||||
var notificationImplementors = []string{"Notification"}
|
||||
|
||||
func (ec *executionContext) _Notification(ctx context.Context, sel ast.SelectionSet, obj *models.Notification) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.RequestContext, sel, notificationImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
var invalids uint32
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Notification")
|
||||
case "key":
|
||||
out.Values[i] = ec._Notification_key(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "type":
|
||||
out.Values[i] = ec._Notification_type(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "header":
|
||||
out.Values[i] = ec._Notification_header(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "content":
|
||||
out.Values[i] = ec._Notification_content(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "progress":
|
||||
out.Values[i] = ec._Notification_progress(ctx, field, obj)
|
||||
case "positive":
|
||||
out.Values[i] = ec._Notification_positive(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "negative":
|
||||
out.Values[i] = ec._Notification_negative(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch()
|
||||
if invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var photoImplementors = []string{"Photo"}
|
||||
|
||||
func (ec *executionContext) _Photo(ctx context.Context, sel ast.SelectionSet, obj *models.Photo) graphql.Marshaler {
|
||||
@@ -6295,6 +6774,26 @@ func (ec *executionContext) _SiteInfo(ctx context.Context, sel ast.SelectionSet,
|
||||
return out
|
||||
}
|
||||
|
||||
var subscriptionImplementors = []string{"Subscription"}
|
||||
|
||||
func (ec *executionContext) _Subscription(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.RequestContext, sel, subscriptionImplementors)
|
||||
ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
|
||||
Object: "Subscription",
|
||||
})
|
||||
if len(fields) != 1 {
|
||||
ec.Errorf(ctx, "must subscribe to exactly one stream")
|
||||
return nil
|
||||
}
|
||||
|
||||
switch fields[0].Name {
|
||||
case "notification":
|
||||
return ec._Subscription_notification(ctx, fields[0])
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(fields[0].Name))
|
||||
}
|
||||
}
|
||||
|
||||
var userImplementors = []string{"User"}
|
||||
|
||||
func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj *models.User) graphql.Marshaler {
|
||||
@@ -6675,6 +7174,29 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNNotification2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐNotification(ctx context.Context, sel ast.SelectionSet, v models.Notification) graphql.Marshaler {
|
||||
return ec._Notification(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNNotification2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐNotification(ctx context.Context, sel ast.SelectionSet, v *models.Notification) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !ec.HasError(graphql.GetResolverContext(ctx)) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._Notification(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNNotificationType2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐNotificationType(ctx context.Context, v interface{}) (models.NotificationType, error) {
|
||||
var res models.NotificationType
|
||||
return res, res.UnmarshalGQL(v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNNotificationType2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐNotificationType(ctx context.Context, sel ast.SelectionSet, v models.NotificationType) graphql.Marshaler {
|
||||
return v
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPhoto2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhoto(ctx context.Context, sel ast.SelectionSet, v models.Photo) graphql.Marshaler {
|
||||
return ec._Photo(ctx, sel, &v)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@ type Filter struct {
|
||||
Offset *int `json:"offset"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Key string `json:"key"`
|
||||
Type NotificationType `json:"type"`
|
||||
Header string `json:"header"`
|
||||
Content string `json:"content"`
|
||||
Progress *float64 `json:"progress"`
|
||||
Positive bool `json:"positive"`
|
||||
Negative bool `json:"negative"`
|
||||
}
|
||||
|
||||
type PhotoDownload struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
@@ -63,6 +73,47 @@ type SiteInfo struct {
|
||||
InitialSetup bool `json:"initialSetup"`
|
||||
}
|
||||
|
||||
type NotificationType string
|
||||
|
||||
const (
|
||||
NotificationTypeMessage NotificationType = "Message"
|
||||
NotificationTypeProgress NotificationType = "Progress"
|
||||
)
|
||||
|
||||
var AllNotificationType = []NotificationType{
|
||||
NotificationTypeMessage,
|
||||
NotificationTypeProgress,
|
||||
}
|
||||
|
||||
func (e NotificationType) IsValid() bool {
|
||||
switch e {
|
||||
case NotificationTypeMessage, NotificationTypeProgress:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e NotificationType) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *NotificationType) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = NotificationType(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid NotificationType", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e NotificationType) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type OrderDirection string
|
||||
|
||||
const (
|
||||
|
||||
64
api/graphql/notification/Notification.go
Normal file
64
api/graphql/notification/Notification.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package notification
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type NotificationChannel = <-chan *models.Notification
|
||||
|
||||
type NotificationListener struct {
|
||||
listenerID int
|
||||
user models.User
|
||||
channel NotificationChannel
|
||||
}
|
||||
|
||||
func NewListener(user models.User, channel NotificationChannel) *NotificationListener {
|
||||
nextNotificationId++
|
||||
return &NotificationListener{
|
||||
listenerID: nextNotificationId,
|
||||
user: user,
|
||||
channel: channel,
|
||||
}
|
||||
}
|
||||
|
||||
var notificationListeners []*NotificationListener = make([]*NotificationListener, 0)
|
||||
var nextNotificationId = 0
|
||||
var notificationLock = &sync.Mutex{}
|
||||
|
||||
func RegisterListener(user *models.User, channel NotificationChannel) int {
|
||||
log.Println("Registering notification listener")
|
||||
|
||||
notificationLock.Lock()
|
||||
defer notificationLock.Unlock()
|
||||
|
||||
notificationListeners = append(notificationListeners, NewListener(*user, channel))
|
||||
return nextNotificationId
|
||||
}
|
||||
|
||||
func DeregisterListener(listenerID int) error {
|
||||
|
||||
notificationLock.Lock()
|
||||
defer notificationLock.Unlock()
|
||||
|
||||
for i, listener := range notificationListeners {
|
||||
if listener.listenerID == listenerID {
|
||||
|
||||
if len(notificationListeners) > 1 {
|
||||
lastIndex := len(notificationListeners) - 1
|
||||
lastListener := notificationListeners[lastIndex]
|
||||
notificationListeners[i] = lastListener
|
||||
notificationListeners[lastIndex] = nil
|
||||
notificationListeners = notificationListeners[:lastIndex]
|
||||
} else {
|
||||
notificationListeners = make([]*NotificationListener, 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("ListenerID not found, while trying to deregister it")
|
||||
}
|
||||
27
api/graphql/resolvers/notification.go
Normal file
27
api/graphql/resolvers/notification.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||
"github.com/viktorstrate/photoview/api/graphql/notification"
|
||||
)
|
||||
|
||||
func (r *subscriptionResolver) Notification(ctx context.Context) (notification.NotificationChannel, error) {
|
||||
|
||||
user := auth.UserFromContext(ctx)
|
||||
if user == nil {
|
||||
return nil, auth.ErrUnauthorized
|
||||
}
|
||||
|
||||
notificationChannel := make(notification.NotificationChannel, 1)
|
||||
|
||||
listenerID := notification.RegisterListener(user, notificationChannel)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
notification.DeregisterListener(listenerID)
|
||||
}()
|
||||
|
||||
return notificationChannel, nil
|
||||
}
|
||||
@@ -22,10 +22,20 @@ func (r *Resolver) Query() api.QueryResolver {
|
||||
return &queryResolver{r}
|
||||
}
|
||||
|
||||
func (r *Resolver) Subscription() api.SubscriptionResolver {
|
||||
return &subscriptionResolver{
|
||||
Resolver: r,
|
||||
}
|
||||
}
|
||||
|
||||
type mutationResolver struct{ *Resolver }
|
||||
|
||||
type queryResolver struct{ *Resolver }
|
||||
|
||||
type subscriptionResolver struct {
|
||||
Resolver *Resolver
|
||||
}
|
||||
|
||||
func (r *queryResolver) SiteInfo(ctx context.Context) (*models.SiteInfo, error) {
|
||||
return models.GetSiteInfo(r.Database)
|
||||
}
|
||||
|
||||
@@ -85,6 +85,25 @@ type Mutation {
|
||||
deleteUser(id: Int!): User @isAdmin
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
notification: Notification!
|
||||
}
|
||||
|
||||
enum NotificationType {
|
||||
Message
|
||||
Progress
|
||||
}
|
||||
|
||||
type Notification {
|
||||
key: String!
|
||||
type: NotificationType!
|
||||
header: String!
|
||||
content: String!
|
||||
progress: Float
|
||||
positive: Boolean!
|
||||
negative: Boolean!
|
||||
}
|
||||
|
||||
type AuthorizeResult {
|
||||
success: Boolean!
|
||||
status: String!
|
||||
|
||||
@@ -8,15 +8,16 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
)
|
||||
|
||||
func PhotoRoutes(db *sql.DB) chi.Router {
|
||||
router := chi.NewRouter()
|
||||
router.Get("/{name}", func(w http.ResponseWriter, r *http.Request) {
|
||||
image_name := chi.URLParam(r, "name")
|
||||
func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
|
||||
|
||||
router.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) {
|
||||
image_name := mux.Vars(r)["name"]
|
||||
|
||||
row := db.QueryRow("SELECT photo_url.purpose, photo.path, photo.photo_id, photo.album_id, photo_url.content_type FROM photo_url, photo WHERE photo_url.photo_name = ? AND photo_url.photo_id = photo.photo_id", image_name)
|
||||
|
||||
@@ -129,6 +130,4 @@ func PhotoRoutes(db *sql.DB) chi.Router {
|
||||
|
||||
io.Copy(w, file)
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
@@ -7,14 +7,16 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
// "github.com/go-chi/chi/middleware"
|
||||
// "github.com/go-chi/cors"
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/database"
|
||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||
"github.com/viktorstrate/photoview/api/routes"
|
||||
"github.com/viktorstrate/photoview/api/server"
|
||||
|
||||
"github.com/99designs/gqlgen/handler"
|
||||
photoview_graphql "github.com/viktorstrate/photoview/api/graphql"
|
||||
@@ -44,18 +46,20 @@ func main() {
|
||||
log.Fatalf("Could not migrate database: %s\n", err)
|
||||
}
|
||||
|
||||
router := chi.NewRouter()
|
||||
router.Use(auth.Middleware(db))
|
||||
rootRouter := mux.NewRouter()
|
||||
rootRouter.Use(auth.Middleware(db))
|
||||
|
||||
router.Use(middleware.Logger)
|
||||
// router.Use(middleware.Logger)
|
||||
|
||||
router.Use(cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"http://localhost:4001", "http://localhost:1234", "*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
|
||||
AllowCredentials: true,
|
||||
Debug: false,
|
||||
}).Handler)
|
||||
rootRouter.Use(server.CORSMiddleware(devMode))
|
||||
|
||||
// router.Use(cors.New(cors.Options{
|
||||
// AllowedOrigins: []string{"http://localhost:4001", "http://localhost:1234", "*"},
|
||||
// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
// AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
|
||||
// AllowCredentials: true,
|
||||
// Debug: false,
|
||||
// }).Handler)
|
||||
|
||||
graphqlResolver := resolvers.Resolver{Database: db}
|
||||
graphqlDirective := photoview_graphql.DirectiveRoot{}
|
||||
@@ -72,19 +76,20 @@ func main() {
|
||||
endpointURL, _ = url.Parse("/")
|
||||
}
|
||||
|
||||
router.Route(endpointURL.Path, func(router chi.Router) {
|
||||
endpointRouter := rootRouter.PathPrefix(endpointURL.Path).Subrouter()
|
||||
|
||||
if devMode {
|
||||
router.Handle("/", handler.Playground("GraphQL playground", path.Join(endpointURL.Path, "/graphql")))
|
||||
endpointRouter.Handle("/", handler.Playground("GraphQL playground", path.Join(endpointURL.Path, "/graphql")))
|
||||
} else {
|
||||
router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
endpointRouter.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte("photoview api endpoint"))
|
||||
})
|
||||
}
|
||||
|
||||
router.Handle("/graphql", handler.GraphQL(photoview_graphql.NewExecutableSchema(graphqlConfig), handler.IntrospectionEnabled(devMode)))
|
||||
endpointRouter.Handle("/graphql", handler.GraphQL(photoview_graphql.NewExecutableSchema(graphqlConfig), handler.IntrospectionEnabled(devMode)))
|
||||
|
||||
router.Mount("/photo", routes.PhotoRoutes(db))
|
||||
})
|
||||
photoRouter := endpointRouter.PathPrefix("/photo").Subrouter()
|
||||
routes.RegisterPhotoRoutes(db, photoRouter)
|
||||
|
||||
if devMode {
|
||||
log.Printf("🚀 Graphql playground ready at %s", endpointURL.String())
|
||||
@@ -92,5 +97,5 @@ func main() {
|
||||
log.Printf("Photoview API endpoint available at %s", endpointURL.String())
|
||||
}
|
||||
|
||||
log.Fatal(http.ListenAndServe(":"+port, router))
|
||||
log.Fatal(http.ListenAndServe(":"+port, rootRouter))
|
||||
}
|
||||
48
api/server/cors_middleware.go
Normal file
48
api/server/cors_middleware.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func CORSMiddleware(devMode bool) mux.MiddlewareFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
methods := []string{http.MethodGet, http.MethodPost, http.MethodOptions}
|
||||
headers := []string{"authorization", "content-type"}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ","))
|
||||
w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
|
||||
|
||||
endpoint, err := url.Parse(os.Getenv("API_ENDPOINT"))
|
||||
if err != nil {
|
||||
log.Fatalln("Could not parse API_ENDPOINT environment variable as url")
|
||||
}
|
||||
endpoint.Path = path.Join(endpoint.Path, "graphql")
|
||||
|
||||
if devMode {
|
||||
// Development environment
|
||||
w.Header().Set("Access-Control-Allow-Origin", req.Header.Get("origin"))
|
||||
w.Header().Set("Vary", "Origin")
|
||||
} else {
|
||||
// Production environment
|
||||
publicEndpoint, err := url.Parse(os.Getenv("PUBLIC_ENDPOINT"))
|
||||
if err != nil {
|
||||
log.Printf("Error parsing environment variable PUBLIC_ENDPOINT as url: %s", err)
|
||||
} else {
|
||||
w.Header().Set("Access-Control-Allow-Origin", publicEndpoint.Scheme+"://"+publicEndpoint.Host)
|
||||
}
|
||||
}
|
||||
|
||||
if req.Method != http.MethodOptions {
|
||||
next.ServeHTTP(w, req)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user