package main import ( "context" "errors" "flag" "log/slog" "net/http" "os" "os/signal" ) func main() { listen := flag.String("listen", envDefault("CONVERGE_RELAY_LISTEN", ":8080"), "address to listen on") secret := flag.String("secret", envDefault("CONVERGE_RELAY_SECRET", ""), "shared secret for Gitea webhook HMAC verification") flag.Parse() logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo})) slog.SetDefault(logger) broker := NewBroker() go broker.Run() mux := http.NewServeMux() mux.HandleFunc("POST /webhook", webhookHandler(broker, *secret)) mux.HandleFunc("GET /events", sseHandler(broker)) mux.HandleFunc("GET /health", healthHandler) srv := &http.Server{Addr: *listen, Handler: mux} // Graceful shutdown go func() { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt) <-sig slog.Info("shutting down") broker.Stop() srv.Shutdown(context.Background()) }() slog.Info("converge-relay starting", "listen", *listen) if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { slog.Error("server error", "error", err) os.Exit(1) } } func envDefault(key, fallback string) string { if v, ok := os.LookupEnv(key); ok { return v } return fallback } func healthHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.Write([]byte("ok\n")) }