// Command app is the Bounty Board main service. package main import ( "context" "fmt" "log/slog" "os" "os/signal" "syscall" "time" "bountyboard/internal/config" httpx "bountyboard/internal/http" "bountyboard/internal/metrics" "bountyboard/internal/store" ) func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, "fatal:", err) os.Exit(1) } } func run() error { if err := config.LoadDotEnv(".env"); err != nil { return fmt.Errorf("load .env: %w", err) } cfg, err := config.Load() if err != nil { return err } log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) slog.SetDefault(log) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() st, err := store.Connect(ctx, cfg, log) if err != nil { return err } defer func() { closeCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := st.Close(closeCtx); err != nil { log.Error("close mongo", "err", err) } }() reg := metrics.NewRegistry() srv := httpx.New(cfg, log, reg) srv.AddReadinessCheck(httpx.ReadinessCheck{ Name: "mongo", Required: true, Probe: st.Ping, }) return srv.Run(ctx) }