package main import ( "embed" "fmt" "io/fs" "log/slog" "net/http" "os" "time" "git.clustercockpit.org/moebiusband/go-http-skeleton/internal/handlers" "git.clustercockpit.org/moebiusband/go-http-skeleton/internal/middleware" ) //go:embed web/static/* var static embed.FS func init() { _, jsonLogger := os.LookupEnv("JSON_LOGGER") _, debug := os.LookupEnv("DEBUG") var programLevel slog.Level if debug { programLevel = slog.LevelDebug } programLevel = slog.LevelDebug if jsonLogger { jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: programLevel, }) slog.SetDefault(slog.New(jsonHandler)) } else { textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: programLevel, }) slog.SetDefault(slog.New(textHandler)) } slog.Info("Logger initialized", slog.Bool("debug", debug)) } func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } addr := ":" + port mux := http.NewServeMux() // Use an embedded filesystem rooted at "web/static" fs, err := fs.Sub(static, "web/static") if err != nil { slog.Error("Failed to create sub filesystem", "error", err) return } // Serve files from the embedded /web/static directory at /static fileServer := http.FileServer(http.FS(fs)) mux.Handle("GET /static/", http.StripPrefix("/static/", fileServer)) mux.HandleFunc("GET /favicon.ico", func(w http.ResponseWriter, r *http.Request) { data, err := static.ReadFile("web/static/favicon.ico") if err != nil { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/plain") w.Write(data) }) mux.HandleFunc("GET /robots.txt", func(w http.ResponseWriter, r *http.Request) { data, err := static.ReadFile("web/static/robots.txt") if err != nil { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/plain") w.Write(data) }) mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.Write([]byte(`OK`)) }) mux.HandleFunc("GET /", handlers.RootHandler()) chain := &middleware.Chain{} chain.Use(middleware.RecoverMiddleware) wrappedMux := chain.Then(mux) server := &http.Server{ Addr: fmt.Sprintf(":%s", port), Handler: wrappedMux, // Recommended timeouts from // https://blog.cloudflare.com/exposing-go-on-the-internet/ ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 120 * time.Second, } slog.Info("Server listening", "addr", addr) if err := server.ListenAndServe(); err != nil { slog.Error("Server failed to start", "error", err) } }