package main import ( "context" "log" "net/http" "os" "os/signal" "sync" "syscall" "time" "github.com/gorilla/mux" "varde/internal/middleware" "varde/modules/keyserver" "varde/modules/webhook" "varde/modules/webserver" ) func main() { cfg := loadConfig() r := mux.NewRouter() r.Use(middleware.SecurityHeaders) r.HandleFunc("/api/healthz", healthHandler).Methods("GET", "HEAD") var active []Module // Registration order matters: gorilla/mux matches in the order // routes are registered, not by specificity. Webhook and keyserver // use specific paths and must come before webserver's catch-all "/". if cfg.Webhook.Enabled { m := webhook.New(cfg.Webhook, cfg.Ntfy) m.RegisterRoutes(r) active = append(active, m) } if cfg.Keyserver.Enabled { m := keyserver.New(cfg.Keyserver) m.RegisterRoutes(r) active = append(active, m) } if cfg.Webserver.Enabled { m := webserver.New(cfg.Webserver) m.RegisterRoutes(r) // catch-all — must stay last active = append(active, m) } ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup for _, m := range active { wg.Add(1) go func(m Module) { defer wg.Done() if err := m.Start(ctx); err != nil { log.Printf("module %s: start error: %v", m.Name(), err) } }(m) } srv := &http.Server{ Addr: ":" + cfg.Port, Handler: r, } go func() { log.Printf("Listening on :%s", cfg.Port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("server error: %v", err) } }() stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT) <-stop log.Println("Shutdown signal received, draining connections...") cancel() shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() if err := srv.Shutdown(shutdownCtx); err != nil { log.Printf("graceful shutdown failed: %v", err) } for _, m := range active { if err := m.Stop(shutdownCtx); err != nil { log.Printf("module %s: stop error: %v", m.Name(), err) } } wg.Wait() log.Println("Server shut down cleanly") } func healthHandler(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) }