diff --git a/codebase/app/graphql_server/graphql_handler.go b/codebase/app/graphql_server/graphql_handler.go new file mode 100644 index 00000000..018f58de --- /dev/null +++ b/codebase/app/graphql_server/graphql_handler.go @@ -0,0 +1,305 @@ +package graphqlserver + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "os" + "reflect" + "runtime" + "strconv" + + "github.com/golangid/candi/candihelper" + "github.com/golangid/candi/candishared" + "github.com/golangid/candi/codebase/app/graphql_server/ws" + "github.com/golangid/candi/codebase/factory" + "github.com/golangid/candi/logger" + "github.com/golangid/candi/tracer" + "github.com/golangid/graphql-go" + gqltypes "github.com/golangid/graphql-go/types" +) + +// Handler interface +type Handler interface { + ServeGraphQL() http.HandlerFunc + ServePlayground(resp http.ResponseWriter, req *http.Request) + ServeVoyager(resp http.ResponseWriter, req *http.Request) +} + +// ConstructHandlerFromService for create public graphql handler (maybe inject to rest handler) +func ConstructHandlerFromService(service factory.ServiceFactory, opt Option) Handler { + + // create dynamic struct + queryResolverValues := make(map[string]interface{}) + mutationResolverValues := make(map[string]interface{}) + subscriptionResolverValues := make(map[string]interface{}) + var queryResolverFields, mutationResolverFields, subscriptionResolverFields []reflect.StructField + for _, m := range service.GetModules() { + if resolverModule := m.GraphQLHandler(); resolverModule != nil { + rootName := string(m.Name()) + query, mutation, subscription := resolverModule.Query(), resolverModule.Mutation(), resolverModule.Subscription() + + appendStructField(rootName, query, &queryResolverFields) + appendStructField(rootName, mutation, &mutationResolverFields) + appendStructField(rootName, subscription, &subscriptionResolverFields) + + queryResolverValues[rootName] = query + mutationResolverValues[rootName] = mutation + subscriptionResolverValues[rootName] = subscription + } + } + + opt.rootResolver.rootQuery = constructStruct(queryResolverFields, queryResolverValues) + opt.rootResolver.rootMutation = constructStruct(mutationResolverFields, mutationResolverValues) + opt.rootResolver.rootSubscription = constructStruct(subscriptionResolverFields, subscriptionResolverValues) + gqlSchema := candihelper.LoadAllFile(os.Getenv(candihelper.WORKDIR)+"api/graphql", ".graphql") + + // default directive + directiveFuncs := map[string]gqltypes.DirectiveFunc{ + "auth": service.GetDependency().GetMiddleware().GraphQLAuth, + "permissionACL": service.GetDependency().GetMiddleware().GraphQLPermissionACL, + } + for directive, dirFunc := range opt.directiveFuncs { + directiveFuncs[directive] = dirFunc + } + + schemaOpts := []graphql.SchemaOpt{ + graphql.UseStringDescriptions(), + graphql.UseFieldResolvers(), + graphql.Tracer(&graphqlTracer{}), + graphql.Logger(&panicLogger{}), + graphql.DirectiveFuncs(directiveFuncs), + } + if opt.DisableIntrospection { + // handling vulnerabilities exploit schema + schemaOpts = append(schemaOpts, graphql.DisableIntrospection()) + } + schema := graphql.MustParseSchema(string(gqlSchema), &opt.rootResolver, schemaOpts...) + + logger.LogYellow(fmt.Sprintf("[GraphQL] endpoint\t\t\t: http://127.0.0.1:%d%s%s", opt.httpPort, opt.rootPath, rootGraphQLPath)) + logger.LogYellow(fmt.Sprintf("[GraphQL] playground\t\t\t: http://127.0.0.1:%d%s%s", opt.httpPort, opt.rootPath, rootGraphQLPlayground)) + logger.LogYellow(fmt.Sprintf("[GraphQL] playground (with explorer)\t: http://127.0.0.1:%d%s%s?graphiql=true", opt.httpPort, opt.rootPath, rootGraphQLPlayground)) + logger.LogYellow(fmt.Sprintf("[GraphQL] voyager\t\t\t: http://127.0.0.1:%d%s%s", opt.httpPort, opt.rootPath, rootGraphQLVoyager)) + + return &handlerImpl{ + disableIntrospection: opt.DisableIntrospection, + schema: schema, + } +} + +type handlerImpl struct { + disableIntrospection bool + schema *graphql.Schema +} + +func NewHandler(disableIntrospection bool, schema *graphql.Schema) Handler { + return &handlerImpl{ + disableIntrospection: disableIntrospection, + schema: schema, + } +} + +func (s *handlerImpl) ServeGraphQL() http.HandlerFunc { + + return ws.NewHandlerFunc(s.schema, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + + var params struct { + Query string `json:"query"` + OperationName string `json:"operationName"` + Variables map[string]interface{} `json:"variables"` + } + body, err := io.ReadAll(req.Body) + if err != nil { + http.Error(resp, err.Error(), http.StatusBadRequest) + return + } + if err := json.Unmarshal(body, ¶ms); err != nil { + params.Query = string(body) + } + + req.Header.Set(candihelper.HeaderXRealIP, extractRealIPHeader(req)) + + ctx := context.WithValue(req.Context(), candishared.ContextKeyHTTPHeader, req.Header) + response := s.schema.Exec(ctx, params.Query, params.OperationName, params.Variables) + responseJSON, err := json.Marshal(response) + if err != nil { + http.Error(resp, err.Error(), http.StatusInternalServerError) + return + } + + resp.Header().Set(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON) + resp.Write(responseJSON) + })) +} + +func (s *handlerImpl) ServePlayground(resp http.ResponseWriter, req *http.Request) { + if s.disableIntrospection { + http.Error(resp, "Forbidden", http.StatusForbidden) + return + } + + if ok, _ := strconv.ParseBool(req.URL.Query().Get("graphiql")); ok { + resp.Write([]byte(` + + + + Candi GraphiQL + + + + + +
+ + +`)) + return + } + + resp.Write([]byte(` + + + + + + + + + Playground + + + +
+ + +`)) +} + +func (s *handlerImpl) ServeVoyager(resp http.ResponseWriter, req *http.Request) { + if s.disableIntrospection { + http.Error(resp, "Forbidden", http.StatusForbidden) + return + } + resp.Write([]byte(` + + + + + + + + + + + + + + + +
Loading...
+ + +`)) +} + +// panicLogger is the default logger used to log panics that occur during query execution +type panicLogger struct{} + +// LogPanic is used to log recovered panic values that occur during query execution +// https://github.com/graph-gophers/graphql-go/blob/master/log/log.go#L19 + custom add log to trace +func (l *panicLogger) LogPanic(ctx context.Context, value interface{}) { + const size = 2 << 10 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + + tracer.Log(ctx, "gql_panic", value) + tracer.Log(ctx, "gql_panic_trace", buf) +} + +func extractRealIPHeader(req *http.Request) string { + for _, header := range []string{candihelper.HeaderXForwardedFor, candihelper.HeaderXRealIP} { + if ip := req.Header.Get(header); ip != "" { + return ip + } + } + + ip, _, _ := net.SplitHostPort(req.RemoteAddr) + return ip +} diff --git a/codebase/app/graphql_server/graphql_server.go b/codebase/app/graphql_server/graphql_server.go index 66799c90..1d8a5ae8 100644 --- a/codebase/app/graphql_server/graphql_server.go +++ b/codebase/app/graphql_server/graphql_server.go @@ -2,37 +2,18 @@ package graphqlserver import ( "context" - "encoding/json" "fmt" - "io" "log" "net" "net/http" - "os" - "reflect" - "runtime" - - "github.com/golangid/candi/candihelper" - "github.com/golangid/candi/candishared" - "github.com/golangid/candi/codebase/app/graphql_server/static" - "github.com/golangid/candi/codebase/app/graphql_server/ws" + "github.com/golangid/candi/codebase/factory" "github.com/golangid/candi/codebase/factory/types" - "github.com/golangid/candi/logger" - "github.com/golangid/candi/tracer" "github.com/golangid/candi/wrapper" - graphql "github.com/golangid/graphql-go" - gqltypes "github.com/golangid/graphql-go/types" "github.com/soheilhy/cmux" ) -const ( - rootGraphQLPath = "/graphql" - rootGraphQLPlayground = "/graphql/playground" - rootGraphQLVoyager = "/graphql/voyager" -) - type graphqlServer struct { opt Option httpEngine *http.Server @@ -51,7 +32,7 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe opt(&server.opt) } - httpHandler := NewHandler(service, server.opt) + httpHandler := ConstructHandlerFromService(service, server.opt) mux := http.NewServeMux() mux.Handle("/", server.opt.rootHandler) @@ -60,12 +41,9 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe mux.HandleFunc(server.opt.rootPath+rootGraphQLPlayground, httpHandler.ServePlayground) mux.HandleFunc(server.opt.rootPath+rootGraphQLVoyager, httpHandler.ServeVoyager) - httpEngine.Addr = server.opt.httpPort + httpEngine.Addr = fmt.Sprintf(":%d", server.opt.httpPort) httpEngine.Handler = mux - logger.LogYellow("[GraphQL] endpoint : " + server.opt.rootPath + rootGraphQLPath) - logger.LogYellow("[GraphQL] playground : " + server.opt.rootPath + rootGraphQLPlayground) - logger.LogYellow("[GraphQL] voyager : " + server.opt.rootPath + rootGraphQLVoyager) fmt.Printf("\x1b[34;1m⇨ GraphQL HTTP server run at port [::]%s\x1b[0m\n\n", httpEngine.Addr) if server.opt.sharedListener != nil { @@ -83,9 +61,8 @@ func (s *graphqlServer) Serve() { err = s.httpEngine.ListenAndServe() } - switch e := err.(type) { - case *net.OpError: - panic(fmt.Errorf("gql http server: %v", e)) + if err != nil { + log.Panicf("GraphQL Server: Unexpected Error: %v", err) } } @@ -101,141 +78,3 @@ func (s *graphqlServer) Shutdown(ctx context.Context) { func (s *graphqlServer) Name() string { return string(types.GraphQL) } - -// Handler interface -type Handler interface { - ServeGraphQL() http.HandlerFunc - ServePlayground(resp http.ResponseWriter, req *http.Request) - ServeVoyager(resp http.ResponseWriter, req *http.Request) -} - -// NewHandler for create public graphql handler (maybe inject to rest handler) -func NewHandler(service factory.ServiceFactory, opt Option) Handler { - - // create dynamic struct - queryResolverValues := make(map[string]interface{}) - mutationResolverValues := make(map[string]interface{}) - subscriptionResolverValues := make(map[string]interface{}) - var queryResolverFields, mutationResolverFields, subscriptionResolverFields []reflect.StructField - for _, m := range service.GetModules() { - if resolverModule := m.GraphQLHandler(); resolverModule != nil { - rootName := string(m.Name()) - query, mutation, subscription := resolverModule.Query(), resolverModule.Mutation(), resolverModule.Subscription() - - appendStructField(rootName, query, &queryResolverFields) - appendStructField(rootName, mutation, &mutationResolverFields) - appendStructField(rootName, subscription, &subscriptionResolverFields) - - queryResolverValues[rootName] = query - mutationResolverValues[rootName] = mutation - subscriptionResolverValues[rootName] = subscription - } - } - - opt.rootResolver.rootQuery = constructStruct(queryResolverFields, queryResolverValues) - opt.rootResolver.rootMutation = constructStruct(mutationResolverFields, mutationResolverValues) - opt.rootResolver.rootSubscription = constructStruct(subscriptionResolverFields, subscriptionResolverValues) - gqlSchema := candihelper.LoadAllFile(os.Getenv(candihelper.WORKDIR)+"api/graphql", ".graphql") - - // default directive - directiveFuncs := map[string]gqltypes.DirectiveFunc{ - "auth": service.GetDependency().GetMiddleware().GraphQLAuth, - "permissionACL": service.GetDependency().GetMiddleware().GraphQLPermissionACL, - } - for directive, dirFunc := range opt.directiveFuncs { - directiveFuncs[directive] = dirFunc - } - - schemaOpts := []graphql.SchemaOpt{ - graphql.UseStringDescriptions(), - graphql.UseFieldResolvers(), - graphql.Tracer(&graphqlTracer{}), - graphql.Logger(&panicLogger{}), - graphql.DirectiveFuncs(directiveFuncs), - } - if opt.DisableIntrospection { - // handling vulnerabilities exploit schema - schemaOpts = append(schemaOpts, graphql.DisableIntrospection()) - } - schema := graphql.MustParseSchema(string(gqlSchema), &opt.rootResolver, schemaOpts...) - - return &handlerImpl{ - disableIntrospection: opt.DisableIntrospection, - schema: schema, - } -} - -type handlerImpl struct { - disableIntrospection bool - schema *graphql.Schema -} - -func (s *handlerImpl) ServeGraphQL() http.HandlerFunc { - - return ws.NewHandlerFunc(s.schema, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - - var params struct { - Query string `json:"query"` - OperationName string `json:"operationName"` - Variables map[string]interface{} `json:"variables"` - } - body, err := io.ReadAll(req.Body) - if err != nil { - http.Error(resp, err.Error(), http.StatusBadRequest) - return - } - if err := json.Unmarshal(body, ¶ms); err != nil { - params.Query = string(body) - } - - ip := req.Header.Get(candihelper.HeaderXForwardedFor) - if ip == "" { - ip = req.Header.Get(candihelper.HeaderXRealIP) - if ip == "" { - ip, _, _ = net.SplitHostPort(req.RemoteAddr) - } - } - req.Header.Set(candihelper.HeaderXRealIP, ip) - - ctx := context.WithValue(req.Context(), candishared.ContextKeyHTTPHeader, req.Header) - response := s.schema.Exec(ctx, params.Query, params.OperationName, params.Variables) - responseJSON, err := json.Marshal(response) - if err != nil { - http.Error(resp, err.Error(), http.StatusInternalServerError) - return - } - - resp.Header().Set(candihelper.HeaderContentType, candihelper.HeaderMIMEApplicationJSON) - resp.Write(responseJSON) - })) -} - -func (s *handlerImpl) ServePlayground(resp http.ResponseWriter, req *http.Request) { - if s.disableIntrospection { - http.Error(resp, "Forbidden", http.StatusForbidden) - return - } - resp.Write([]byte(static.PlaygroundAsset)) -} - -func (s *handlerImpl) ServeVoyager(resp http.ResponseWriter, req *http.Request) { - if s.disableIntrospection { - http.Error(resp, "Forbidden", http.StatusForbidden) - return - } - resp.Write([]byte(static.VoyagerAsset)) -} - -// panicLogger is the default logger used to log panics that occur during query execution -type panicLogger struct{} - -// LogPanic is used to log recovered panic values that occur during query execution -// https://github.com/graph-gophers/graphql-go/blob/master/log/log.go#L19 + custom add log to trace -func (l *panicLogger) LogPanic(ctx context.Context, value interface{}) { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - - tracer.Log(ctx, "gql_panic", value) - tracer.Log(ctx, "gql_panic_trace", buf) -} diff --git a/codebase/app/graphql_server/option.go b/codebase/app/graphql_server/option.go index de02b052..c8519831 100644 --- a/codebase/app/graphql_server/option.go +++ b/codebase/app/graphql_server/option.go @@ -1,7 +1,6 @@ package graphqlserver import ( - "fmt" "net/http" "github.com/golangid/candi/wrapper" @@ -9,12 +8,18 @@ import ( "github.com/soheilhy/cmux" ) +const ( + rootGraphQLPath = "/graphql" + rootGraphQLPlayground = "/graphql/playground" + rootGraphQLVoyager = "/graphql/voyager" +) + type ( // Option gql server Option struct { DisableIntrospection bool - httpPort string + httpPort uint16 rootPath string debugMode bool jaegerMaxPacketSize int @@ -30,7 +35,7 @@ type ( func getDefaultOption() Option { return Option{ - httpPort: ":8000", + httpPort: 8000, rootPath: "", debugMode: true, rootHandler: http.HandlerFunc(wrapper.HTTPHandlerDefaultRoot), @@ -40,7 +45,7 @@ func getDefaultOption() Option { // SetHTTPPort option func func SetHTTPPort(port uint16) OptionFunc { return func(o *Option) { - o.httpPort = fmt.Sprintf(":%d", port) + o.httpPort = port } } @@ -110,11 +115,11 @@ func SetRootSubscription(resolver interface{}) OptionFunc { } // AddDirectiveFunc option func -func AddDirectiveFunc(directiveName string, dirFuncs types.DirectiveFunc) OptionFunc { +func AddDirectiveFunc(directiveName string, handlerFunc types.DirectiveFunc) OptionFunc { return func(o *Option) { if o.directiveFuncs == nil { o.directiveFuncs = make(map[string]types.DirectiveFunc) } - o.directiveFuncs[directiveName] = dirFuncs + o.directiveFuncs[directiveName] = handlerFunc } } diff --git a/codebase/app/graphql_server/static/static_template.go b/codebase/app/graphql_server/static/static_template.go deleted file mode 100644 index 09bc750f..00000000 --- a/codebase/app/graphql_server/static/static_template.go +++ /dev/null @@ -1,113 +0,0 @@ -package static - -const ( - // PlaygroundAsset template - PlaygroundAsset = ` - - - - - - - - - - Playground - - - -
- - - ` - - // VoyagerAsset template - VoyagerAsset = ` - - - - - - - - - - - - - - - -
Loading...
- - -` -) diff --git a/codebase/app/grpc_server/grpc_server.go b/codebase/app/grpc_server/grpc_server.go index 4af9a974..f51bfb7b 100644 --- a/codebase/app/grpc_server/grpc_server.go +++ b/codebase/app/grpc_server/grpc_server.go @@ -98,7 +98,7 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe func (s *grpcServer) Serve() { if err := s.serverEngine.Serve(s.listener); err != nil { - log.Println("GRPC: Unexpected Error", err) + log.Panicf("GRPC: Unexpected Error: %v", err) } } diff --git a/codebase/app/rest_server/option.go b/codebase/app/rest_server/option.go index 511010d5..5da01d9f 100644 --- a/codebase/app/rest_server/option.go +++ b/codebase/app/rest_server/option.go @@ -1,7 +1,6 @@ package restserver import ( - "fmt" "net/http" "os" @@ -14,18 +13,17 @@ import ( type ( option struct { - rootMiddlewares []echo.MiddlewareFunc - rootHandler http.Handler - errorHandler echo.HTTPErrorHandler - httpPort string - rootPath string - debugMode bool - includeGraphQL bool - graphqlDisableIntrospection bool - jaegerMaxPacketSize int - sharedListener cmux.CMux - engineOption func(e *echo.Echo) - graphqlOption graphqlserver.Option + rootMiddlewares []echo.MiddlewareFunc + rootHandler http.Handler + errorHandler echo.HTTPErrorHandler + httpPort uint16 + rootPath string + debugMode bool + includeGraphQL bool + jaegerMaxPacketSize int + sharedListener cmux.CMux + engineOption func(e *echo.Echo) + graphqlOption graphqlserver.Option } // OptionFunc type @@ -34,7 +32,7 @@ type ( func getDefaultOption() option { return option{ - httpPort: ":8000", + httpPort: 8000, rootPath: "", debugMode: true, rootMiddlewares: []echo.MiddlewareFunc{ @@ -56,7 +54,7 @@ func getDefaultOption() option { // SetHTTPPort option func func SetHTTPPort(port uint16) OptionFunc { return func(o *option) { - o.httpPort = fmt.Sprintf(":%d", port) + o.httpPort = port } } @@ -95,13 +93,6 @@ func SetIncludeGraphQL(includeGraphQL bool) OptionFunc { } } -// SetGraphQLDisableIntrospection option func -func SetGraphQLDisableIntrospection(graphqlDisableIntrospection bool) OptionFunc { - return func(o *option) { - o.graphqlDisableIntrospection = graphqlDisableIntrospection - } -} - // SetJaegerMaxPacketSize option func func SetJaegerMaxPacketSize(max int) OptionFunc { return func(o *option) { diff --git a/codebase/app/rest_server/rest_server.go b/codebase/app/rest_server/rest_server.go index 424c4447..0a505357 100644 --- a/codebase/app/rest_server/rest_server.go +++ b/codebase/app/rest_server/rest_server.go @@ -74,17 +74,13 @@ func NewServer(service factory.ServiceFactory, opts ...OptionFunc) factory.AppSe // inject graphql handler to rest server if server.opt.includeGraphQL { - graphqlHandler := graphqlserver.NewHandler(service, server.opt.graphqlOption) + graphqlHandler := graphqlserver.ConstructHandlerFromService(service, server.opt.graphqlOption) server.serverEngine.Any(server.opt.rootPath+"/graphql", echo.WrapHandler(graphqlHandler.ServeGraphQL())) server.serverEngine.GET(server.opt.rootPath+"/graphql/playground", echo.WrapHandler(http.HandlerFunc(graphqlHandler.ServePlayground))) server.serverEngine.GET(server.opt.rootPath+"/graphql/voyager", echo.WrapHandler(http.HandlerFunc(graphqlHandler.ServeVoyager))) - - logger.LogYellow("[GraphQL] endpoint : " + server.opt.rootPath + "/graphql") - logger.LogYellow("[GraphQL] playground : " + server.opt.rootPath + "/graphql/playground") - logger.LogYellow("[GraphQL] voyager : " + server.opt.rootPath + "/graphql/voyager") } - fmt.Printf("\x1b[34;1m⇨ HTTP server run at port [::]%s\x1b[0m\n\n", server.opt.httpPort) + fmt.Printf("\x1b[34;1m⇨ HTTP server run at port [::]:%d\x1b[0m\n\n", server.opt.httpPort) return server } @@ -99,12 +95,11 @@ func (h *restServer) Serve() { h.serverEngine.Listener = h.listener err = h.serverEngine.Start("") } else { - err = h.serverEngine.Start(h.opt.httpPort) + err = h.serverEngine.Start(fmt.Sprintf(":%d", h.opt.httpPort)) } - switch e := err.(type) { - case *net.OpError: - panic(fmt.Errorf("rest server: %v", e)) + if err != nil { + log.Panicf("REST Server: Unexpected Error: %v", err) } } diff --git a/codebase/app/task_queue_worker/graphql_resolver.go b/codebase/app/task_queue_worker/graphql_resolver.go index 04ef4512..ecaf1002 100644 --- a/codebase/app/task_queue_worker/graphql_resolver.go +++ b/codebase/app/task_queue_worker/graphql_resolver.go @@ -4,24 +4,22 @@ import ( "context" "errors" "fmt" + "log" "net/http" "runtime" "sort" "strings" "time" + "github.com/golangid/candi" dashboard "github.com/golangid/candi-plugin/task-queue-worker/dashboard" "github.com/golangid/candi/candihelper" + "github.com/golangid/candi/candishared" + graphqlserver "github.com/golangid/candi/codebase/app/graphql_server" + "github.com/golangid/candi/config/env" "github.com/golangid/candi/logger" "github.com/golangid/graphql-go" - "github.com/golangid/graphql-go/relay" "github.com/golangid/graphql-go/trace" - - "github.com/golangid/candi" - "github.com/golangid/candi/candishared" - "github.com/golangid/candi/codebase/app/graphql_server/static" - "github.com/golangid/candi/codebase/app/graphql_server/ws" - "github.com/golangid/candi/config/env" ) func (t *taskQueueWorker) serveGraphQLAPI() { @@ -38,16 +36,17 @@ func (t *taskQueueWorker) serveGraphQLAPI() { mux.Handle("/job", t.opt.basicAuth(http.StripPrefix("/", http.FileServer(dashboard.Dashboard)))) mux.Handle("/expired", t.opt.basicAuth(http.StripPrefix("/", http.FileServer(dashboard.Dashboard)))) - mux.HandleFunc("/graphql", ws.NewHandlerFunc(schema, &relay.Handler{Schema: schema})) - mux.HandleFunc("/voyager", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte(static.VoyagerAsset)) }) - mux.HandleFunc("/playground", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte(static.PlaygroundAsset)) }) + gqlHandler := graphqlserver.NewHandler(false, schema) + mux.HandleFunc("/graphql", gqlHandler.ServeGraphQL()) + mux.HandleFunc("/playground", gqlHandler.ServePlayground) + mux.HandleFunc("/voyager", gqlHandler.ServeVoyager) httpEngine := new(http.Server) httpEngine.Addr = fmt.Sprintf(":%d", engine.opt.dashboardPort) httpEngine.Handler = mux if err := httpEngine.ListenAndServe(); err != nil { - panic(fmt.Errorf("task queue worker dashboard: %v", err)) + log.Panicf("task queue worker dashboard: %v", err) } } diff --git a/codebase/factory/appfactory/setup_rest_server.go b/codebase/factory/appfactory/setup_rest_server.go index bff66dc4..1d49ee0b 100644 --- a/codebase/factory/appfactory/setup_rest_server.go +++ b/codebase/factory/appfactory/setup_rest_server.go @@ -1,6 +1,7 @@ package appfactory import ( + graphqlserver "github.com/golangid/candi/codebase/app/graphql_server" restserver "github.com/golangid/candi/codebase/app/rest_server" "github.com/golangid/candi/codebase/factory" "github.com/golangid/candi/config/env" @@ -16,6 +17,12 @@ func SetupRESTServer(service factory.ServiceFactory, opts ...restserver.OptionFu restserver.SetDebugMode(env.BaseEnv().DebugMode), restserver.SetJaegerMaxPacketSize(env.BaseEnv().JaegerMaxPacketSize), } + if env.BaseEnv().UseGraphQL { + restOptions = append(restOptions, restserver.AddGraphQLOption( + graphqlserver.SetDisableIntrospection(env.BaseEnv().GraphQLDisableIntrospection), + graphqlserver.SetHTTPPort(env.BaseEnv().HTTPPort), + )) + } restOptions = append(restOptions, opts...) return restserver.NewServer(service, restOptions...) } diff --git a/init.go b/init.go index 7645cd23..f2e421b4 100644 --- a/init.go +++ b/init.go @@ -2,5 +2,5 @@ package candi const ( // Version of this library - Version = "v1.14.4" + Version = "v1.14.5" )