2019-03-24 14:57:29 +01:00
|
|
|
package serv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2019-04-08 08:47:59 +02:00
|
|
|
"log"
|
2019-03-24 14:57:29 +01:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/dosco/super-graph/psql"
|
|
|
|
"github.com/dosco/super-graph/qcode"
|
|
|
|
"github.com/go-pg/pg"
|
2019-03-25 05:43:14 +01:00
|
|
|
"github.com/gobuffalo/flect"
|
2019-03-24 14:57:29 +01:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
)
|
|
|
|
|
2019-04-06 08:35:08 +02:00
|
|
|
//go:generate esc -o static.go -ignore \\.DS_Store -prefix ../web/build -private -pkg serv ../web/build
|
2019-03-28 14:38:05 +01:00
|
|
|
|
2019-03-24 14:57:29 +01:00
|
|
|
const (
|
|
|
|
authFailBlockAlways = iota + 1
|
|
|
|
authFailBlockPerQuery
|
|
|
|
authFailBlockNever
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
logger *logrus.Logger
|
2019-04-08 08:47:59 +02:00
|
|
|
conf *config
|
2019-03-24 14:57:29 +01:00
|
|
|
db *pg.DB
|
|
|
|
pcompile *psql.Compiler
|
|
|
|
qcompile *qcode.Compiler
|
|
|
|
authFailBlock int
|
|
|
|
)
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
type config struct {
|
|
|
|
Env string
|
|
|
|
HostPort string `mapstructure:"host_port"`
|
|
|
|
WebUI bool `mapstructure:"web_ui"`
|
|
|
|
DebugLevel int `mapstructure:"debug_level"`
|
|
|
|
EnableTracing bool `mapstructure:"enable_tracing"`
|
|
|
|
AuthFailBlock string `mapstructure:"auth_fail_block"`
|
|
|
|
Inflections map[string]string
|
|
|
|
|
|
|
|
Auth struct {
|
|
|
|
Type string
|
|
|
|
Cookie string
|
|
|
|
Header string
|
|
|
|
|
2019-04-10 07:38:48 +02:00
|
|
|
Rails struct {
|
|
|
|
Version string
|
2019-04-08 08:47:59 +02:00
|
|
|
SecretKeyBase string `mapstructure:"secret_key_base"`
|
2019-04-10 07:38:48 +02:00
|
|
|
URL string
|
|
|
|
Password string
|
|
|
|
MaxIdle int `mapstructure:"max_idle"`
|
|
|
|
MaxActive int `mapstructure:"max_active"`
|
|
|
|
Salt string
|
|
|
|
SignSalt string `mapstructure:"sign_salt"`
|
|
|
|
AuthSalt string `mapstructure:"auth_salt"`
|
|
|
|
}
|
2019-04-08 08:47:59 +02:00
|
|
|
|
|
|
|
JWT struct {
|
|
|
|
Provider string
|
|
|
|
Secret string
|
|
|
|
PubKeyFile string `mapstructure:"public_key_file"`
|
|
|
|
PubKeyType string `mapstructure:"public_key_type"`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DB struct {
|
|
|
|
Type string
|
|
|
|
Host string
|
|
|
|
Port string
|
|
|
|
DBName string
|
|
|
|
User string
|
|
|
|
Password string
|
|
|
|
PoolSize int `mapstructure:"pool_size"`
|
|
|
|
MaxRetries int `mapstructure:"max_retries"`
|
|
|
|
LogLevel string `mapstructure:"log_level"`
|
|
|
|
|
|
|
|
Variables map[string]string
|
|
|
|
|
|
|
|
Defaults struct {
|
|
|
|
Filter []string
|
|
|
|
Blacklist []string
|
|
|
|
}
|
|
|
|
|
|
|
|
Fields []struct {
|
|
|
|
Name string
|
|
|
|
Filter []string
|
|
|
|
Table string
|
|
|
|
Blacklist []string
|
|
|
|
}
|
|
|
|
} `mapstructure:"database"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func initLog() *logrus.Logger {
|
|
|
|
log := logrus.New()
|
|
|
|
log.Formatter = new(logrus.TextFormatter)
|
|
|
|
log.Formatter.(*logrus.TextFormatter).DisableColors = false
|
|
|
|
log.Formatter.(*logrus.TextFormatter).DisableTimestamp = true
|
|
|
|
log.Level = logrus.TraceLevel
|
|
|
|
log.Out = os.Stdout
|
|
|
|
|
|
|
|
return log
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
func initConf() (*config, error) {
|
|
|
|
vi := viper.New()
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
path := flag.String("path", "./", "Path to config files")
|
2019-03-24 14:57:29 +01:00
|
|
|
flag.Parse()
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.SetEnvPrefix("SG")
|
|
|
|
vi.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
vi.AutomaticEnv()
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.AddConfigPath(*path)
|
2019-04-09 14:43:42 +02:00
|
|
|
vi.AddConfigPath("./config")
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.SetConfigName(getConfigName())
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.SetDefault("host_port", "0.0.0.0:8080")
|
|
|
|
vi.SetDefault("web_ui", false)
|
|
|
|
vi.SetDefault("debug_level", 0)
|
|
|
|
vi.SetDefault("enable_tracing", false)
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.SetDefault("database.type", "postgres")
|
|
|
|
vi.SetDefault("database.host", "localhost")
|
|
|
|
vi.SetDefault("database.port", 5432)
|
|
|
|
vi.SetDefault("database.user", "postgres")
|
|
|
|
vi.SetDefault("database.password", "")
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.SetDefault("env", "development")
|
|
|
|
vi.BindEnv("env", "GO_ENV")
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
vi.SetDefault("auth.rails_redis.max_idle", 80)
|
|
|
|
vi.SetDefault("auth.rails_redis.max_active", 12000)
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
if err := vi.ReadInConfig(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
c := &config{}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
if err := vi.Unmarshal(c); err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to decode config, %v", err)
|
|
|
|
}
|
2019-04-01 14:55:46 +02:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
for k, v := range c.Inflections {
|
|
|
|
flect.AddPlural(k, v)
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
authFailBlock = getAuthFailBlock(c)
|
|
|
|
|
|
|
|
//fmt.Printf("%#v", c)
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
return c, nil
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
func initDB(c *config) (*pg.DB, error) {
|
2019-03-24 14:57:29 +01:00
|
|
|
opt := &pg.Options{
|
2019-04-08 08:47:59 +02:00
|
|
|
Addr: strings.Join([]string{c.DB.Host, c.DB.Port}, ":"),
|
|
|
|
User: c.DB.User,
|
|
|
|
Password: c.DB.Password,
|
|
|
|
Database: c.DB.DBName,
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
if c.DB.PoolSize != 0 {
|
|
|
|
opt.PoolSize = conf.DB.PoolSize
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
if c.DB.MaxRetries != 0 {
|
|
|
|
opt.MaxRetries = c.DB.MaxRetries
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
db := pg.Connect(opt)
|
|
|
|
if db == nil {
|
|
|
|
return nil, errors.New("failed to connect to postgres db")
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
2019-04-08 08:47:59 +02:00
|
|
|
|
|
|
|
return db, nil
|
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) {
|
|
|
|
cdb := c.DB
|
|
|
|
|
|
|
|
fm := make(map[string][]string, len(cdb.Fields))
|
2019-04-09 03:24:29 +02:00
|
|
|
tmap := make(map[string]string, len(cdb.Fields))
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
for i := range cdb.Fields {
|
|
|
|
f := cdb.Fields[i]
|
2019-04-09 03:24:29 +02:00
|
|
|
name := flect.Pluralize(strings.ToLower(f.Name))
|
|
|
|
if len(f.Filter) != 0 {
|
|
|
|
if f.Filter[0] == "none" {
|
|
|
|
fm[name] = []string{}
|
|
|
|
} else {
|
|
|
|
fm[name] = f.Filter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(f.Table) != 0 {
|
|
|
|
tmap[name] = f.Table
|
|
|
|
}
|
2019-04-08 08:47:59 +02:00
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
qc, err := qcode.NewCompiler(qcode.Config{
|
|
|
|
Filter: cdb.Defaults.Filter,
|
|
|
|
FilterMap: fm,
|
|
|
|
Blacklist: cdb.Defaults.Blacklist,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
|
|
|
schema, err := psql.NewDBSchema(db)
|
|
|
|
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-04-09 03:24:29 +02:00
|
|
|
Schema: schema,
|
|
|
|
Vars: cdb.Variables,
|
|
|
|
TableMap: tmap,
|
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
|
|
|
}
|
|
|
|
|
|
|
|
func InitAndListen() {
|
2019-04-08 08:47:59 +02:00
|
|
|
var err error
|
|
|
|
|
|
|
|
logger = initLog()
|
|
|
|
|
|
|
|
conf, err = initConf()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
db, err = initDB(conf)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
qcompile, pcompile, err = initCompilers(conf)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
|
|
|
http.HandleFunc("/api/v1/graphql", withAuth(apiv1Http))
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
if conf.WebUI {
|
2019-03-28 14:38:05 +01:00
|
|
|
http.Handle("/", http.FileServer(_escFS(false)))
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-08 08:47:59 +02:00
|
|
|
fmt.Printf("Super-Graph listening on %s (%s)\n",
|
|
|
|
conf.HostPort, conf.Env)
|
|
|
|
|
|
|
|
logger.Fatal(http.ListenAndServe(conf.HostPort, nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
func getConfigName() string {
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
|
|
|
return "dev"
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|