112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
"github.com/supabase/auth/internal/api"
|
|
"github.com/supabase/auth/internal/conf"
|
|
"github.com/supabase/auth/internal/reloader"
|
|
"github.com/supabase/auth/internal/storage"
|
|
"github.com/supabase/auth/internal/utilities"
|
|
)
|
|
|
|
var serveCmd = cobra.Command{
|
|
Use: "serve",
|
|
Long: "Start API server",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
serve(cmd.Context())
|
|
},
|
|
}
|
|
|
|
func serve(ctx context.Context) {
|
|
if err := conf.LoadFile(configFile); err != nil {
|
|
logrus.WithError(err).Fatal("unable to load config")
|
|
}
|
|
|
|
if err := conf.LoadDirectory(watchDir); err != nil {
|
|
logrus.WithError(err).Fatal("unable to load config from watch dir")
|
|
}
|
|
|
|
config, err := conf.LoadGlobalFromEnv()
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("unable to load config")
|
|
}
|
|
|
|
db, err := storage.Dial(config)
|
|
if err != nil {
|
|
logrus.Fatalf("error opening database: %+v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
addr := net.JoinHostPort(config.API.Host, config.API.Port)
|
|
logrus.Infof("GoTrue API started on: %s", addr)
|
|
|
|
opts := []api.Option{
|
|
api.NewLimiterOptions(config),
|
|
}
|
|
a := api.NewAPIWithVersion(config, db, utilities.Version, opts...)
|
|
ah := reloader.NewAtomicHandler(a)
|
|
|
|
baseCtx, baseCancel := context.WithCancel(context.Background())
|
|
defer baseCancel()
|
|
|
|
httpSrv := &http.Server{
|
|
Addr: addr,
|
|
Handler: ah,
|
|
ReadHeaderTimeout: 2 * time.Second, // to mitigate a Slowloris attack
|
|
BaseContext: func(net.Listener) context.Context {
|
|
return baseCtx
|
|
},
|
|
}
|
|
log := logrus.WithField("component", "api")
|
|
|
|
var wg sync.WaitGroup
|
|
defer wg.Wait() // Do not return to caller until this goroutine is done.
|
|
|
|
if watchDir != "" {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
fn := func(latestCfg *conf.GlobalConfiguration) {
|
|
log.Info("reloading api with new configuration")
|
|
latestAPI := api.NewAPIWithVersion(
|
|
latestCfg, db, utilities.Version, opts...)
|
|
ah.Store(latestAPI)
|
|
}
|
|
|
|
rl := reloader.NewReloader(watchDir)
|
|
if err := rl.Watch(ctx, fn); err != nil {
|
|
log.WithError(err).Error("watcher is exiting")
|
|
}
|
|
}()
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
<-ctx.Done()
|
|
|
|
defer baseCancel() // close baseContext
|
|
|
|
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer shutdownCancel()
|
|
|
|
if err := httpSrv.Shutdown(shutdownCtx); err != nil && !errors.Is(err, context.Canceled) {
|
|
log.WithError(err).Error("shutdown failed")
|
|
}
|
|
}()
|
|
|
|
if err := httpSrv.ListenAndServe(); err != http.ErrServerClosed {
|
|
log.WithError(err).Fatal("http server listen failed")
|
|
}
|
|
}
|