2020-04-10 08:27:43 +02:00
|
|
|
package serv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/dosco/super-graph/core"
|
2020-04-16 06:26:32 +02:00
|
|
|
"github.com/dosco/super-graph/internal/serv/internal/auth"
|
2020-04-10 08:27:43 +02:00
|
|
|
"github.com/rs/cors"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
maxReadBytes = 100000 // 100Kb
|
|
|
|
introspectionQuery = "IntrospectionQuery"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errUnauthorized = errors.New("not authorized")
|
|
|
|
)
|
|
|
|
|
|
|
|
type gqlReq struct {
|
|
|
|
OpName string `json:"operationName"`
|
|
|
|
Query string `json:"query"`
|
|
|
|
Vars json.RawMessage `json:"variables"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type errorResp struct {
|
|
|
|
Error error `json:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func apiV1Handler() http.Handler {
|
|
|
|
h, err := auth.WithAuth(http.HandlerFunc(apiV1), &conf.Auth)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("ERR %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(conf.AllowedOrigins) != 0 {
|
|
|
|
c := cors.New(cors.Options{
|
|
|
|
AllowedOrigins: conf.AllowedOrigins,
|
|
|
|
AllowCredentials: true,
|
|
|
|
Debug: conf.DebugCORS,
|
|
|
|
})
|
|
|
|
h = c.Handler(h)
|
|
|
|
}
|
|
|
|
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
func apiV1(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ct := r.Context()
|
|
|
|
|
|
|
|
//nolint: errcheck
|
|
|
|
if conf.AuthFailBlock && !auth.IsAuth(ct) {
|
|
|
|
renderErr(w, errUnauthorized, nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(io.LimitReader(r.Body, maxReadBytes))
|
|
|
|
if err != nil {
|
|
|
|
renderErr(w, err, nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
req := gqlReq{}
|
|
|
|
|
|
|
|
err = json.Unmarshal(b, &req)
|
|
|
|
if err != nil {
|
|
|
|
renderErr(w, err, nil)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.EqualFold(req.OpName, introspectionQuery) {
|
|
|
|
introspect(w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
res, err := sg.GraphQL(ct, req.Query, req.Vars)
|
|
|
|
|
2020-04-11 08:45:06 +02:00
|
|
|
if logLevel >= LogLevelDebug {
|
2020-04-10 08:27:43 +02:00
|
|
|
log.Printf("DBG query:\n%s\nsql:\n%s", req.Query, res.SQL())
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
renderErr(w, err, res)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
|
2020-04-11 08:45:06 +02:00
|
|
|
if logLevel >= LogLevelInfo {
|
2020-04-10 08:27:43 +02:00
|
|
|
zlog.Info("success",
|
|
|
|
zap.String("op", res.Operation()),
|
|
|
|
zap.String("name", res.QueryName()),
|
|
|
|
zap.String("role", res.Role()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint: errcheck
|
|
|
|
func renderErr(w http.ResponseWriter, err error, res *core.Result) {
|
|
|
|
if err == errUnauthorized {
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
}
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(&errorResp{err})
|
|
|
|
|
2020-04-11 08:45:06 +02:00
|
|
|
if logLevel >= LogLevelError {
|
2020-04-10 08:27:43 +02:00
|
|
|
if res != nil {
|
|
|
|
zlog.Error(err.Error(),
|
|
|
|
zap.String("op", res.Operation()),
|
|
|
|
zap.String("name", res.QueryName()),
|
|
|
|
zap.String("role", res.Role()),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
zlog.Error(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|