2019-03-24 14:57:29 +01:00
|
|
|
package serv
|
|
|
|
|
|
|
|
import (
|
2019-05-13 01:27:26 +02:00
|
|
|
"context"
|
2019-03-24 14:57:29 +01:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2019-05-13 01:27:26 +02:00
|
|
|
"os/signal"
|
2019-03-24 14:57:29 +01:00
|
|
|
"strings"
|
2019-05-13 01:27:26 +02:00
|
|
|
"time"
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-09-27 08:19:24 +02:00
|
|
|
rice "github.com/GeertJohan/go.rice"
|
2019-03-24 14:57:29 +01:00
|
|
|
"github.com/dosco/super-graph/psql"
|
|
|
|
"github.com/dosco/super-graph/qcode"
|
2019-10-06 22:28:10 +02:00
|
|
|
"github.com/gobuffalo/flect"
|
2019-03-24 14:57:29 +01:00
|
|
|
)
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
func initCompilers(c *config) (*qcode.Compiler, *psql.Compiler, error) {
|
2019-06-14 06:32:15 +02:00
|
|
|
schema, err := psql.NewDBSchema(db, c.getAliasMap())
|
2019-05-13 01:27:26 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
2019-04-08 08:47:59 +02:00
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-10-06 22:28:10 +02:00
|
|
|
conf := qcode.Config{
|
2019-05-13 01:27:26 +02:00
|
|
|
DefaultFilter: c.DB.Defaults.Filter,
|
2019-10-06 22:28:10 +02:00
|
|
|
FilterMap: qcode.Filters{
|
|
|
|
All: make(map[string][]string, len(c.Tables)),
|
|
|
|
Query: make(map[string][]string, len(c.Tables)),
|
|
|
|
Insert: make(map[string][]string, len(c.Tables)),
|
|
|
|
Update: make(map[string][]string, len(c.Tables)),
|
|
|
|
Delete: make(map[string][]string, len(c.Tables)),
|
|
|
|
},
|
|
|
|
Blocklist: c.DB.Defaults.Blocklist,
|
|
|
|
KeepArgs: false,
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range c.Tables {
|
|
|
|
t := c.Tables[i]
|
|
|
|
|
|
|
|
singular := flect.Singularize(t.Name)
|
|
|
|
plural := flect.Pluralize(t.Name)
|
|
|
|
|
|
|
|
setFilter := func(fm map[string][]string, fil []string) {
|
|
|
|
switch {
|
|
|
|
case len(fil) == 0:
|
|
|
|
return
|
|
|
|
case fil[0] == "none" || len(fil[0]) == 0:
|
|
|
|
fm[singular] = []string{}
|
|
|
|
fm[plural] = []string{}
|
|
|
|
default:
|
|
|
|
fm[singular] = t.Filter
|
|
|
|
fm[plural] = t.Filter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setFilter(conf.FilterMap.All, t.Filter)
|
|
|
|
setFilter(conf.FilterMap.Query, t.FilterQuery)
|
|
|
|
setFilter(conf.FilterMap.Insert, t.FilterInsert)
|
|
|
|
setFilter(conf.FilterMap.Update, t.FilterUpdate)
|
|
|
|
setFilter(conf.FilterMap.Delete, t.FilterDelete)
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-10-06 22:28:10 +02:00
|
|
|
qc, err := qcode.NewCompiler(conf)
|
2019-03-24 14:57:29 +01:00
|
|
|
if err != nil {
|
2019-04-08 08:47:59 +02:00
|
|
|
return nil, nil, err
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
pc := psql.NewCompiler(psql.Config{
|
2019-06-14 06:32:15 +02:00
|
|
|
Schema: schema,
|
2019-07-29 07:13:33 +02:00
|
|
|
Vars: c.getVariables(),
|
2019-04-08 08:47:59 +02:00
|
|
|
})
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
return qc, pc, nil
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-09-27 08:19:24 +02:00
|
|
|
func initWatcher(cpath string) {
|
2019-09-08 20:56:32 +02:00
|
|
|
if conf.WatchAndReload == false {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var d dir
|
2019-09-27 08:19:24 +02:00
|
|
|
if len(cpath) == 0 || cpath == "./" {
|
2019-09-08 20:56:32 +02:00
|
|
|
d = Dir("./config", ReExec)
|
|
|
|
} else {
|
2019-09-27 08:19:24 +02:00
|
|
|
d = Dir(cpath, ReExec)
|
2019-09-08 20:56:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err := Do(logger.Printf, d)
|
|
|
|
if err != nil {
|
2019-09-27 08:19:24 +02:00
|
|
|
logger.Fatal().Err(err).Send()
|
2019-09-08 20:56:32 +02:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
func startHTTP() {
|
2019-07-30 07:38:05 +02:00
|
|
|
hp := strings.SplitN(conf.HostPort, ":", 2)
|
|
|
|
|
|
|
|
if len(conf.Host) != 0 {
|
|
|
|
hp[0] = conf.Host
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(conf.Port) != 0 {
|
|
|
|
hp[1] = conf.Port
|
|
|
|
}
|
|
|
|
|
|
|
|
hostPort := fmt.Sprintf("%s:%s", hp[0], hp[1])
|
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
srv := &http.Server{
|
2019-07-30 07:38:05 +02:00
|
|
|
Addr: hostPort,
|
2019-05-13 01:27:26 +02:00
|
|
|
Handler: routeHandler(),
|
|
|
|
ReadTimeout: 5 * time.Second,
|
|
|
|
WriteTimeout: 10 * time.Second,
|
|
|
|
MaxHeaderBytes: 1 << 20,
|
|
|
|
}
|
|
|
|
|
|
|
|
idleConnsClosed := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
sigint := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigint, os.Interrupt)
|
|
|
|
<-sigint
|
|
|
|
|
|
|
|
if err := srv.Shutdown(context.Background()); err != nil {
|
2019-06-14 06:32:15 +02:00
|
|
|
logger.Error().Err(err).Msg("shutdown signal received")
|
2019-05-13 01:27:26 +02:00
|
|
|
}
|
|
|
|
close(idleConnsClosed)
|
|
|
|
}()
|
|
|
|
|
|
|
|
srv.RegisterOnShutdown(func() {
|
2019-09-26 06:35:31 +02:00
|
|
|
db.Close()
|
2019-05-13 01:27:26 +02:00
|
|
|
})
|
|
|
|
|
2019-07-30 07:38:05 +02:00
|
|
|
fmt.Printf("%s listening on %s (%s)\n", serverName, hostPort, conf.Env)
|
2019-05-13 01:27:26 +02:00
|
|
|
|
|
|
|
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
2019-06-14 06:32:15 +02:00
|
|
|
logger.Error().Err(err).Msg("server closed")
|
2019-05-13 01:27:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
<-idleConnsClosed
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
func routeHandler() http.Handler {
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
|
|
|
mux.Handle("/api/v1/graphql", withAuth(apiv1Http))
|
2019-04-08 08:47:59 +02:00
|
|
|
if conf.WebUI {
|
2019-09-27 08:19:24 +02:00
|
|
|
mux.Handle("/", http.FileServer(rice.MustFindBox("../web/build").HTTPBox()))
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Server", serverName)
|
|
|
|
mux.ServeHTTP(w, r)
|
|
|
|
}
|
2019-04-08 08:47:59 +02:00
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
return http.HandlerFunc(fn)
|
2019-04-08 08:47:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func getConfigName() string {
|
2019-09-29 07:49:13 +02:00
|
|
|
if len(os.Getenv("GO_ENV")) == 0 {
|
|
|
|
return "dev"
|
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
ge := strings.ToLower(os.Getenv("GO_ENV"))
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(ge, "pro"):
|
|
|
|
return "prod"
|
|
|
|
|
|
|
|
case strings.HasPrefix(ge, "sta"):
|
|
|
|
return "stage"
|
|
|
|
|
|
|
|
case strings.HasPrefix(ge, "tes"):
|
|
|
|
return "test"
|
2019-09-28 17:34:03 +02:00
|
|
|
|
|
|
|
case strings.HasPrefix(ge, "dev"):
|
|
|
|
return "dev"
|
2019-04-08 08:47:59 +02:00
|
|
|
}
|
|
|
|
|
2019-09-28 17:34:03 +02:00
|
|
|
return ge
|
2019-04-08 08:47:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func getAuthFailBlock(c *config) int {
|
|
|
|
switch c.AuthFailBlock {
|
|
|
|
case "always":
|
|
|
|
return authFailBlockAlways
|
|
|
|
case "per_query", "perquery", "query":
|
|
|
|
return authFailBlockPerQuery
|
|
|
|
case "never", "false":
|
|
|
|
return authFailBlockNever
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
return authFailBlockAlways
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|