package actions import ( "context" "io" "net/http" "os" "os/signal" "strings" "time" "git.t-juice.club/torjus/gpaste" "git.t-juice.club/torjus/gpaste/api" "github.com/urfave/cli/v2" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func ActionServe(c *cli.Context) error { configPath := "gpaste-server.toml" if c.IsSet("config") { configPath = c.String("config") } var cfg *gpaste.ServerConfig var r io.ReadCloser r, err := os.Open(configPath) if err != nil { cfg = &gpaste.ServerConfig{ LogLevel: "INFO", URL: "localhost:8080", ListenAddr: ":8080", SigningSecret: "TODO: CHANGE THIS LOL", Store: &gpaste.ServerStoreConfig{ Type: "memory", }, } } else { defer r.Close() cfg, err = gpaste.ServerConfigFromReader(r) if err != nil { if err != nil { return cli.Exit(err, 1) } } } // Setup loggers rootLogger := getRootLogger(cfg.LogLevel) serverLogger := rootLogger.Named("SERV") accessLogger := rootLogger.Named("ACCS") // Setup contexts for clean shutdown rootCtx, rootCancel := signal.NotifyContext(context.Background(), os.Interrupt) defer rootCancel() httpCtx, httpCancel := context.WithCancel(rootCtx) defer httpCancel() httpShutdownCtx, httpShutdownCancel := context.WithCancel(context.Background()) defer httpShutdownCancel() go func() { srv := api.NewHTTPServer(cfg) srv.Addr = cfg.ListenAddr srv.Logger = serverLogger srv.AccessLogger = accessLogger // Wait for cancel go func() { <-httpCtx.Done() timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() srv.Shutdown(timeoutCtx) }() serverLogger.Infow("Starting HTTP server.", "addr", cfg.ListenAddr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { serverLogger.Errorw("Error during shutdown.", "error", err) } serverLogger.Infow("HTTP server shutdown complete.", "addr", cfg.ListenAddr) httpShutdownCancel() }() <-httpShutdownCtx.Done() return nil } func getRootLogger(level string) *zap.SugaredLogger { logEncoderConfig := zap.NewProductionEncoderConfig() logEncoderConfig.EncodeCaller = zapcore.ShortCallerEncoder logEncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder logEncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder logEncoderConfig.EncodeDuration = zapcore.StringDurationEncoder rootLoggerConfig := &zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), OutputPaths: []string{"stdout"}, ErrorOutputPaths: []string{"stdout"}, Encoding: "console", EncoderConfig: logEncoderConfig, DisableCaller: true, } switch strings.ToUpper(level) { case "DEBUG": rootLoggerConfig.DisableCaller = false rootLoggerConfig.Level = zap.NewAtomicLevelAt(zap.DebugLevel) case "INFO": rootLoggerConfig.Level = zap.NewAtomicLevelAt(zap.InfoLevel) case "WARN", "WARNING": rootLoggerConfig.Level = zap.NewAtomicLevelAt(zap.WarnLevel) case "ERR", "ERROR": rootLoggerConfig.Level = zap.NewAtomicLevelAt(zap.ErrorLevel) } rootLogger, err := rootLoggerConfig.Build() if err != nil { panic(err) } return rootLogger.Sugar() }