github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/gen/main-rest-sqlite3.go (about)

     1  package gen
     2  
     3  import (
     4  	"flag"
     5  	"path/filepath"
     6  )
     7  
     8  func init() {
     9  	globalMapGenerator["main-rest-sqlite3"] = GeneratorFunc(func(s *Settings, name string, args ...string) error {
    10  
    11  		// FIXME: see if we want to move to pflag - it's probably better and
    12  		// would be smart to be consistent
    13  		fset := flag.NewFlagSet("gen", flag.ContinueOnError)
    14  		// storeName := fset.String("name", "Store", "The name of the store struct to define")
    15  		targetFile, data, err := ParseFlagsAndOneFile(s, fset, args)
    16  		if err != nil {
    17  			return err
    18  		}
    19  
    20  		targetDir, _ := filepath.Split(targetFile)
    21  		_, targetDirName := filepath.Split(targetDir)
    22  
    23  		data["TargetDirName"] = targetDirName
    24  
    25  		return OutputGoSrcTemplate(s, data, targetFile, `
    26  package main
    27  
    28  import (
    29  	"github.com/gocraft/dbr"
    30  	"github.com/gocaveman/dbrobj"
    31  
    32  	_ "github.com/mattn/go-sqlite3"
    33  	// _ "github.com/go-sql-driver/mysql"
    34  	// _ "github.com/lib/pq"
    35  )
    36  
    37  const APP_NAME = "{{.TargetDirName}}"
    38  
    39  func main() {
    40  
    41  	var err error
    42  
    43  	// TODO: HTTPS support, including certificates - letsencrypt with option to override
    44  	// and workable solution for local development
    45  	pflag.StringP("http-listen", "l", ":8080", "IP:Port to listen on for HTTP")
    46  	// pflag.StringP("db-dsn", "", "root:@tcp(localhost:3306)/"+APP_NAME+"?charset=utf8mb4,utf8", "Database connection string")
    47  	pflag.StringP("db-dsn", "", "file:"+APP_NAME+"?mode=memory&cache=shared", "Database connection string")
    48  	pflag.StringP("db-driver", "", "sqlite3", "Database driver name")
    49  	// TODO: on the migrations we probably also want a way to separately check, and then update
    50  	// without running the app - so you can build the new version with the schema changes, check it
    51  	// apply it, and then deploy.
    52  	pflag.StringP("db-migrate", "", "auto", "Database migration behavior ('auto' to update, 'check' to report out of date, or 'none' to ignore migrations)")
    53  	pflag.BoolP("debug", "g", false, "Enable debug output (intended for development only)")
    54  	pflag.Parse()
    55  	viper.BindPFlags(pflag.CommandLine)
    56  
    57  	// TODO: add viper config file search and load stuff
    58  
    59  	if wd := viper.GetString("workdir"); wd != "" {
    60  		if err := os.Chdir(wd); err != nil {
    61  			log.Fatalf("Error changing directory to %q: %v", wd, err)
    62  		}
    63  	}
    64  
    65  	hl := webutil.NewDefaultHandlerList()
    66  	for _, item := range handlerregistry.Contents() {
    67  		hl = append(hl, item.Value)
    68  	}
    69  	hl = append(hl, http.NotFoundHandler())
    70  
    71  	dbDriver := viper.GetString("db-driver")
    72  	dbDsn := viper.GetString("db-dsn")
    73  	// TODO: provide in autowire, just need to know the pattern for names...
    74  
    75  	dbrConn, err := dbr.Open(dbDriver, dbDsn, nil)
    76  	if err != nil {
    77  		log.Fatal(err)
    78  	}
    79  	autowire.Provide("", dbrConn)
    80  
    81  	connector := dbrobj.NewConfig().NewConnector(dbrConn, nil)
    82  	autowire.Provide("", connector)
    83  
    84  	err = autowire.Contents().Run()
    85  	if err != nil {
    86  		log.Fatalf("autowire error: %v", err)
    87  	}
    88  
    89  	dbMigrateMode := viper.GetString("db-migrate")
    90  	ml := migrateregistry.Contents().WithDriverName(dbDriver).Sorted()
    91  	// EDITME: you can filter migrations here if needed
    92  	versioner, err := migratedbr.New(dbDriver, dbDsn)
    93  	if err != nil {
    94  		log.Fatal(err)
    95  	}
    96  	runner := migrate.NewRunner(dbDriver, dbDsn, versioner, ml)
    97  	if dbMigrateMode == "check" {
    98  		result, err := runner.CheckAll(true)
    99  		if err != nil {
   100  			log.Fatalf("Migration check error: %v", err)
   101  		}
   102  		log.Printf("Migration check result: %+v", result) // TODO: better output
   103  	} else if dbMigrateMode == "auto" {
   104  		err := runner.RunAllUpToLatest()
   105  		if err != nil {
   106  			log.Fatalf("Migration auto-update error: %v", err)
   107  		}
   108  	}
   109  
   110  	var wg sync.WaitGroup
   111  
   112  	// FIXME: HTTP/HTTPS graceful shutdown;
   113  	// example here https://gist.github.com/peterhellberg/38117e546c217960747aacf689af3dc2
   114  	// The idea is that if we stop receiving new requests and succesfully complete the old,
   115  	// a load balancer can reliably hold onto and retry connections that are rejected
   116  	// and we can get a deploy/restart (even on a single server) with no actual "down time".
   117  	// FIXME: we should also look at runtime.SetFinalizer and anything else relevant and
   118  	// see if there is some sort of "shutdown" hook that can easily be put together - without
   119  	// getting too weird and complicated - maybe it's just a matter of calling Close() on
   120  	// whatever needs it as defer in main and things get cleaned up that way (and possibly
   121  	// autowire needs to support some sort of Close() mechanism as well)
   122  	webutil.StartHTTPServer(&http.Server{
   123  		Addr:    viper.GetString("http-listen"),
   124  		Handler: hl,
   125  	}, &wg)
   126  
   127  	wg.Wait()
   128  }
   129  
   130  `, false)
   131  
   132  	})
   133  }