Add initialization command to setup new apps

This commit is contained in:
Vikram Rangnekar
2019-09-27 02:19:24 -04:00
parent 5e86b445c5
commit ecec2968e9
30 changed files with 1021 additions and 70 deletions

View File

@ -7,6 +7,7 @@ import (
"io/ioutil"
"log"
"os"
"path"
"sort"
"strings"
)
@ -31,20 +32,20 @@ type allowList struct {
active bool
}
func initAllowList(path string) {
func initAllowList(cpath string) {
_allowList = allowList{
list: make(map[string]*allowItem),
saveChan: make(chan *allowItem),
active: true,
}
if len(path) != 0 {
fp := fmt.Sprintf("%s/allow.list", path)
if len(cpath) != 0 {
fp := path.Join(cpath, "allow.list")
if _, err := os.Stat(fp); err == nil {
_allowList.filepath = fp
} else if !os.IsNotExist(err) {
panic(err)
logger.Fatal().Err(err).Send()
}
}
@ -54,7 +55,7 @@ func initAllowList(path string) {
if _, err := os.Stat(fp); err == nil {
_allowList.filepath = fp
} else if !os.IsNotExist(err) {
panic(err)
logger.Fatal().Err(err).Send()
}
}
@ -64,15 +65,25 @@ func initAllowList(path string) {
if _, err := os.Stat(fp); err == nil {
_allowList.filepath = fp
} else if !os.IsNotExist(err) {
panic(err)
logger.Fatal().Err(err).Send()
}
}
if len(_allowList.filepath) == 0 {
panic("allow.list not found")
}
if conf.UseAllowList {
logger.Fatal().Msg("allow.list not found")
}
_allowList.load()
if len(cpath) == 0 {
_allowList.filepath = "./config/allow.list"
} else {
_allowList.filepath = path.Join(cpath, "allow.list")
}
logger.Warn().Msg("allow.list not found")
} else {
_allowList.load()
}
go func() {
for v := range _allowList.saveChan {
@ -182,7 +193,7 @@ func (al *allowList) save(item *allowItem) {
f, err := os.Create(al.filepath)
if err != nil {
logger.Warn().Err(err).Msg("Failed to write allow list to file")
logger.Warn().Err(err).Msgf("Failed to write allow list: %s", al.filepath)
return
}

View File

@ -35,7 +35,7 @@ func jwtHandler(next http.HandlerFunc) http.HandlerFunc {
case len(publicKeyFile) != 0:
kd, err := ioutil.ReadFile(publicKeyFile)
if err != nil {
panic(err)
logger.Fatal().Err(err).Send()
}
switch conf.Auth.JWT.PubKeyType {
@ -51,7 +51,7 @@ func jwtHandler(next http.HandlerFunc) http.HandlerFunc {
}
if err != nil {
panic(err)
logger.Fatal().Err(err).Send()
}
}

View File

@ -28,13 +28,13 @@ func railsRedisHandler(next http.HandlerFunc) http.HandlerFunc {
Dial: func() (redis.Conn, error) {
c, err := redis.DialURL(conf.Auth.Rails.URL)
if err != nil {
panic(err)
logger.Fatal().Err(err).Send()
}
pwd := conf.Auth.Rails.Password
if len(pwd) != 0 {
if _, err := c.Do("AUTH", pwd); err != nil {
panic(err)
logger.Fatal().Err(err).Send()
}
}
return c, err

View File

@ -17,8 +17,6 @@ import (
"github.com/spf13/viper"
)
//go:generate esc -o static.go -ignore \\.DS_Store -prefix ../web/build -private -pkg serv ../web/build
const (
serverName = "Super Graph"
@ -42,11 +40,10 @@ var (
migrateCmd *cobra.Command
statusCmd *cobra.Command
newMigrationCmd *cobra.Command
initCmd *cobra.Command
)
func Init() {
var err error
rootCmd = &cobra.Command{
Use: "super-graph",
Short: "An instant high-performance GraphQL API. No code needed. https://supergraph.dev",
@ -110,6 +107,13 @@ e.g. tern migrate -d last
Run: cmdNewMigration,
}
initCmd = &cobra.Command{
Use: "init APP-NAME",
Short: "Initialize a new application",
Long: "Generate all the required files to start on a new Super Graph app",
Run: cmdInit,
}
logger = initLog()
rootCmd.Flags().StringVar(&confPath,
@ -118,16 +122,13 @@ e.g. tern migrate -d last
//cmdMigrate.Flags().StringVarP(&cliOptions.destinationVersion,
// "destination", "d", "last", "destination migration version")
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(servCmd)
rootCmd.AddCommand(seedCmd)
rootCmd.AddCommand(migrateCmd)
rootCmd.AddCommand(statusCmd)
rootCmd.AddCommand(newMigrationCmd)
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
if err := rootCmd.Execute(); err != nil {
logger.Fatal().Err(err).Send()
}

132
serv/cmd_init.go Normal file
View File

@ -0,0 +1,132 @@
package serv
import (
"bytes"
"io/ioutil"
"os"
"path"
"strings"
"text/template"
rice "github.com/GeertJohan/go.rice"
"github.com/spf13/cobra"
)
func cmdInit(cmd *cobra.Command, args []string) {
if len(args) != 1 {
cmd.Help()
os.Exit(1)
}
tmpl := newTempl(map[string]string{
"app_name": strings.Title(strings.Join(args, " ")),
"app_name_slug": strings.ToLower(strings.Join(args, "_")),
})
// Create app folder and add relevant files
name := args[0]
appPath := path.Join("./", name)
ifNotExists(appPath, func(p string) error {
return os.Mkdir(p, os.ModePerm)
})
ifNotExists(path.Join(appPath, "seed.js"), func(p string) error {
if v, err := tmpl.get("docker-compose.yml"); err == nil {
return ioutil.WriteFile(p, v, 0644)
} else {
return err
}
})
ifNotExists(path.Join(appPath, "docker-compose.yml"), func(p string) error {
if v, err := tmpl.get("docker-compose.yml"); err == nil {
return ioutil.WriteFile(p, v, 0644)
} else {
return err
}
})
// Create app config folder and add relevant files
appConfigPath := path.Join(appPath, "config")
ifNotExists(appConfigPath, func(p string) error {
return os.Mkdir(p, os.ModePerm)
})
ifNotExists(path.Join(appConfigPath, "dev.yml"), func(p string) error {
if v, err := tmpl.get("dev.yml"); err == nil {
return ioutil.WriteFile(p, v, 0644)
} else {
return err
}
})
ifNotExists(path.Join(appConfigPath, "prod.yml"), func(p string) error {
if v, err := tmpl.get("prod.yml"); err == nil {
return ioutil.WriteFile(p, v, 0644)
} else {
return err
}
})
// Create app migrations folder and add relevant files
appMigrationsPath := path.Join(appConfigPath, "migrations")
ifNotExists(appMigrationsPath, func(p string) error {
return os.Mkdir(p, os.ModePerm)
})
ifNotExists(path.Join(appMigrationsPath, "100_init.sql"), func(p string) error {
if v, err := tmpl.get("100_init.sql"); err == nil {
return ioutil.WriteFile(p, v, 0644)
} else {
return err
}
})
logger.Info().Msgf("app '%s' initialized", name)
}
type Templ struct {
*rice.Box
data map[string]string
}
func newTempl(data map[string]string) *Templ {
return &Templ{rice.MustFindBox("../tmpl"), data}
}
func (t *Templ) get(name string) ([]byte, error) {
v := t.MustString(name)
b := bytes.Buffer{}
tm := template.Must(template.New(name).Parse(v))
if err := tm.Execute(&b, t.data); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func ifNotExists(filePath string, doFn func(string) error) {
_, err := os.Stat(filePath)
if err == nil {
logger.Info().Err(err).Msgf("create skipped '%s' exists", filePath)
return
}
if os.IsNotExist(err) == false {
logger.Fatal().Err(err).Msgf("unable to check if '%s' exists", filePath)
}
err = doFn(filePath)
if err != nil {
logger.Fatal().Err(err).Msgf("unable to create '%s'", filePath)
}
logger.Info().Msgf("created '%s'", filePath)
}

View File

@ -16,6 +16,11 @@ import (
func cmdSeed(cmd *cobra.Command, args []string) {
var err error
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
conf.UseAllowList = false
db, err = initDBPool(conf)

View File

@ -7,6 +7,10 @@ import (
func cmdServ(cmd *cobra.Command, args []string) {
var err error
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
db, err = initDBPool(conf)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to database")

View File

@ -40,6 +40,12 @@ func cmdNewMigration(cmd *cobra.Command, args []string) {
os.Exit(1)
}
var err error
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
name := args[0]
m, err := migrate.FindMigrations(conf.MigrationsPath)
@ -64,10 +70,16 @@ func cmdNewMigration(cmd *cobra.Command, args []string) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
logger.Info().Msgf("created migration '%s'\n", mpath)
logger.Info().Msgf("created migration '%s'", mpath)
}
func cmdMigrate(cmd *cobra.Command, args []string) {
var err error
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
conn, err := initDB(conf)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to database")
@ -157,6 +169,12 @@ func cmdMigrate(cmd *cobra.Command, args []string) {
}
func cmdStatus(cmd *cobra.Command, args []string) {
var err error
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
conn, err := initDB(conf)
if err != nil {
logger.Fatal().Err(err).Msg("failed to connect to database")

View File

@ -30,7 +30,7 @@ func initPreparedList() {
for k, v := range _allowList.list {
err := prepareStmt(k, v.gql, v.vars)
if err != nil {
panic(err)
logger.Fatal().Err(err).Send()
}
}
}

View File

@ -161,7 +161,7 @@ func Do(log func(string, ...interface{}), additional ...dir) error {
func ReExec() {
err := syscall.Exec(binSelf, append([]string{binSelf}, os.Args[1:]...), os.Environ())
if err != nil {
panic(fmt.Sprintf("cannot restart: %v", err))
logger.Fatal().Err(err).Msg("cannot restart")
}
}

View File

@ -9,6 +9,7 @@ import (
"strings"
"time"
rice "github.com/GeertJohan/go.rice"
"github.com/dosco/super-graph/psql"
"github.com/dosco/super-graph/qcode"
)
@ -38,22 +39,22 @@ func initCompilers(c *config) (*qcode.Compiler, *psql.Compiler, error) {
return qc, pc, nil
}
func initWatcher(path string) {
func initWatcher(cpath string) {
if conf.WatchAndReload == false {
return
}
var d dir
if len(path) == 0 || path == "./" {
if len(cpath) == 0 || cpath == "./" {
d = Dir("./config", ReExec)
} else {
d = Dir(path, ReExec)
d = Dir(cpath, ReExec)
}
go func() {
err := Do(logger.Printf, d)
if err != nil {
panic(err)
logger.Fatal().Err(err).Send()
}
}()
}
@ -109,7 +110,7 @@ func routeHandler() http.Handler {
mux.Handle("/api/v1/graphql", withAuth(apiv1Http))
if conf.WebUI {
mux.Handle("/", http.FileServer(_escFS(false)))
mux.Handle("/", http.FileServer(rice.MustFindBox("../web/build").HTTPBox()))
}
fn := func(w http.ResponseWriter, r *http.Request) {