package main import ( "context" "errors" "flag" "fmt" "io" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "git.runcible.io/learning/pulley/internal/logging" ) const Version = "1.0.0" type config struct { port int env string logLevel string } type application struct { config config logger *slog.Logger } func run(ctx context.Context, w io.Writer, args []string) error { var cfg config flagSet := flag.NewFlagSet(args[0], flag.ExitOnError) flagSet.IntVar(&cfg.port, "port", 5002, "API server port") flagSet.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)") flagSet.StringVar(&cfg.logLevel, "log-level", "INFO", "Logging Level (INFO|DEBUG|WARN|ERROR)") if err := flagSet.Parse(args[1:]); err != nil { return err } logger := logging.InitLogging(cfg.logLevel, w, true) app := application{config: cfg, logger: logger} mux := http.NewServeMux() mux.HandleFunc("/v1/healthcheck", app.HealthCheckHandler) srv := &http.Server{ Addr: fmt.Sprintf("%s:%d", "0.0.0.0", app.config.port), ErrorLog: slog.NewLogLogger(app.logger.Handler(), slog.LevelError), IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 524288, // 0.5 mb Handler: mux, } slog.Info(fmt.Sprintf("Listening on http://%s", srv.Addr)) defer func() { slog.Debug("Must have hit a graceful shutdown") }() go func() { if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { slog.Error(err.Error()) os.Exit(1) slog.Info("Stopped serving connections") } }() // Handle graceful shutdown sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // Block until signal is received <-sigChan shutdownCtx, shutdownRelease := context.WithTimeout(ctx, 10*time.Second) defer shutdownRelease() if err := srv.Shutdown(shutdownCtx); err != nil { slog.Error("Failed to close within context timeout. Forcing server close.") srv.Close() return err } return nil } func main() { ctx := context.Background() if err := run(ctx, os.Stdout, os.Args); err != nil { slog.Error(err.Error()) os.Exit(1) } }