// Package main is the entrypoint for the mitbringsl backend HTTP server. package main import ( "context" "errors" "log/slog" "net/http" "os" "os/signal" "syscall" "time" "github.com/jackc/pgx/v5/pgxpool" "github.com/mitbringsl/backend/internal/config" "github.com/mitbringsl/backend/internal/httpapi" "github.com/mitbringsl/backend/internal/logging" "github.com/mitbringsl/backend/internal/store" ) func main() { if err := run(); err != nil { slog.Error("fatal error", "error", err) os.Exit(1) } } func run() error { cfg, err := config.Load() if err != nil { return err } logging.Init(cfg.LogLevel) slog.Info("starting mitbringsl backend", "env", cfg.AppEnv, "addr", cfg.HTTPAddr, "google_oidc_enabled", cfg.GoogleOIDC.Enabled, "generic_oidc_enabled", cfg.GenericOIDC.Enabled, ) rootCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() pool, err := store.New(rootCtx, cfg) if err != nil { return err } defer pool.Close() slog.Info("database connected") api := httpapi.NewAPI(cfg, pool) srv := &http.Server{ Addr: cfg.HTTPAddr, Handler: api.Handler(), ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 15 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } errCh := make(chan error, 1) go func() { slog.Info("http server listening", "addr", cfg.HTTPAddr) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { errCh <- err } }() select { case <-rootCtx.Done(): slog.Info("shutdown signal received") case err := <-errCh: return err } shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(shutdownCtx); err != nil { slog.Error("graceful shutdown failed", "error", err) } slog.Info("bye") return nil } // keep pgxpool import referenced even when API holds the pool directly (future-proof). var _ = (*pgxpool.Pool)(nil)